2017-08-11 80 views
1

DataStax blog,我在测试片查询,即使博客已经与卡桑德拉2完成,我们在3卡桑德拉切片查询非常奇怪的结果

所以我定义了一个测试表作为波纹管。

CREATE TABLE foo(
    part_key TEXT, 
    start_ts INT, 
    end_ts INT, 
    PRIMARY KEY(part_key, start_ts, end_ts) 
) WITH CLUSTERING ORDER BY (start_ts ASC, end_ts ASC); 

此表中插入了一些固定装置。

INSERT INTO foo(part_key, start_ts, end_ts) VALUES ('A', 1, 5); 
INSERT INTO foo(part_key, start_ts, end_ts) VALUES ('A', 2, 3); 
INSERT INTO foo(part_key, start_ts, end_ts) VALUES ('B', 4, 7); 
INSERT INTO foo(part_key, start_ts, end_ts) VALUES ('B', 9, 13); 

INSERT INTO foo(part_key, start_ts, end_ts) VALUES ('B', 1, 2); 

INSERT INTO foo(part_key, start_ts, end_ts) 
VALUES ('B', 9, 9999); -- 9999 = symbolic value for no end 

我检查B分区第一:

> SELECT * FROM foo WHERE part_key='B'; 

part_key | start_ts | end_ts 
----------+----------+-------- 
     B |  1 |  2 
     B |  4 |  7 
     B |  9 |  13 
     B |  9 | 9999 

(4 rows) 

我们不能执行“自然”查询SELECT * FROM foo WHERE part_key='B' AND start_ts <= 7 AND end_ts >= 7,因为它会引发以下错误。

InvalidRequest: Error from server: code=2200 [Invalid query] message="Clustering column "end_ts" cannot be restricted (preceding column "start_ts" is restricted by a non-EQ relation)" 

在DataStax博客中有一个查询如下。

SELECT * FROM numberOfRequests 
    WHERE cluster = ‘cluster1’ 
    AND date = ‘2015-06-05’ 
    AND datacenter = 'US_WEST_COAST' 
    AND (hour, minute) >= (12, 30) AND (hour) < (14) 

所以我们尝试这种解决方法有以下切片查询,使用start_ts >= -9999一如既往真实,允许为end_ts指定不平等状况在一起。

SELECT * FROM foo WHERE part_key='B' 
AND (start_ts, end_ts) >= (-9999, 7) AND start_ts <= 7; -- -9999 = min_value 

它的执行没有任何警告,并给出了结果波纹管。

part_key | start_ts | end_ts 
----------+----------+-------- 
     B |  1 |  2 
     B |  4 |  7 

(2 rows) 

在这一点上,该行#1(与start_ts = 1)不应该在那里,我作为end_ts >= 7不是此行如此。由于start_ts >= -9999总是如此,所以第一个条件(start_ts, end_ts) >= (-9999, 7)似乎被忽略:结果与查询SELECT * FROM foo WHERE part_key='B' AND start_ts <= 7相同。

阅读博客的例子,我在想,(至少C2.2),(start_ts, end_ts) >= (-9999, 7) AND start_ts <= 7将意味着start_ts >= -9999 AND end_ts >= 7 AND start_ts <= 7,以相若方式(start_ts, end_ts) = (4, 7)这意味着start_ts = 4 AND end_ts = 7为波纹管。

SELECT * FROM foo WHERE part_key='B' AND (start_ts, end_ts) = (4, 7); 

part_key | start_ts | end_ts 
----------+----------+-------- 
     B |  4 |  7 

(1 rows) 

这样的元组不等式如何被真正解释? 有什么方法可以更新它,使其“工作”?

+0

https:// stackoverflow。com/questions/32646751/cassandra-can not-restrict-2-columns-using-clustering-keyversion-2-1-9 –

+0

这并没有真正回答,或者至少表明Cassandra的结果不一致。 – cchantep

回答

1

让我们插入一些记录

INSERT INTO foo (part_key, start_ts , end_ts) VALUES ('B', 1, 7); 
INSERT INTO foo (part_key, start_ts , end_ts) VALUES ('B', 4, 8); 
INSERT INTO foo (part_key, start_ts , end_ts) VALUES ('B', 9, 7); 

现在我们有part_key数据= 'B'

cqlsh:test> SELECT * FROM foo WHERE part_key='B' ; 

part_key | start_ts | end_ts 
----------+----------+-------- 
     B |  1 |  2 
     B |  1 |  7 
     B |  4 |  7 
     B |  4 |  8 
     B |  9 |  7 
     B |  9 |  13 
     B |  9 | 9999 

现在让我们来查询这个数据:

cqlsh:test> SELECT * FROM foo WHERE part_key='B' AND (start_ts, end_ts) >= (1, 4) AND (start_ts, end_ts) <= (9, 7); 

part_key | start_ts | end_ts 
----------+----------+-------- 
     B |  1 |  7 
     B |  4 |  7 
     B |  4 |  8 
     B |  9 |  7 

这似乎结果不正确。但事实并非如此。我们的理解不正确。

卡桑德拉将存储您的数据按复合字段排序(start_ts,end_ts)首先按start_ts排序,然后为每个start_ts排序end_ts。当我们用(start_ts, end_ts) >= (1, 4) AND (start_ts, end_ts) <= (9, 7)查询。卡桑德拉治疗(start_ts, end_ts)作为一个单一的复合字段,并将其值介于(1,4)至(9,7)

part_key | start_ts | end_ts 
----------+----------+-------- 
     B |  1 |  2 
-------------------------------> start range 
     B |  1 |  7 
     B |  4 |  7 
     B |  4 |  8 
     B |  9 |  7 
-------------------------------> end range  
     B |  9 |  13 
     B |  9 | 9999 

希望大家理解。

+0

谢谢,它证实Cassandra无法按日期/范围查询 – cchantep

+0

是的,您必须从客户端进行过滤 –

+0

这是卡桑德拉只能找到匹配数据的唯一方法:-) – cchantep

1

你在这里打的是通过元组进行比较 - 因为你已经将你的两列合并为一个元组,所以你得到的比较行为稍微不同于预期。这是正确的,这不符合你的期望。

(start_ts, end_ts) >= (-9999, 7) 

这并不意味着start_ts >= -9999 && end_ts >= 7,这意味着绑定的左(含)的元组(-9999,7)。有可能是一个元组(-9998,1)这是元组方面比(-9999,7)更大,即使end_ts==1小于7

-1

一种可能的方法是在你的第二列使用过滤。不幸的是,从3.6开始,仅支持对群集列进行过滤。因此,如果您的版本小于3.0.x版本,则可以通过将第二个集群列转换为常规集群列来解决问题。在这种情况下,下面的查询会给你你期望的结果:

SELECT * FROM foo WHERE part_key='B' AND start_ts = 7 ALLOW FILTERING 

现在,我不知道你的数据和它的基数使过滤可能不是一个很好的选择。

另一种选择是更改数据模型。对事物进行建模有不同的方法来以高效的方式满足您的需求。

-2

其全部关于ORDER的元组不是VALUE s个别列。 简而言之,它不是比较值,而是比较元组的顺序。一旦Cassandra根据您的聚类顺序对元组进行排序,它就会比较元组的顺序。例如,在给定集合的分区B: (start_ts,end_ts)> =(1,1)和(start_ts,end_ts)< =(7,7)

首先这个逻辑AND的部分:(start_ts, (1,1)元组下的所有元组:(1,2),(4,7),(9,13),(9,9999)

逻辑AND的第二部分:(start_ts,end_ts)< =(7,7)将包括所有在此(7,7)元组之上的元组:(1,2),(4,7)

由于在这种设计中,代码中总会需要进一步检查。例如,如果您的表具有开始和结束日期列,并且您使用StartDate ASC和EndDate ASC对其进行了聚类,并且您正在查找FromDate到ToDate范围之间的所有行,您将始终需要进一步过滤代码中的行。为此,在where子句中,您将具有: (StartDate,EndDate)> =(FromDate,FromDate)AND(StartDate,EndDate)< =(ToDate,ToDate)。

这会不会是相当于SQL条件: 起始日期> =没有fromdate日期和结束日期< = TODATE

希望这有助于。

+0

做一些格式化 – Billa