DML

本节介绍DML语法的说明和示例。

SELECT

查询数据。

语法

select_statement ::=  SELECT [ JSON | HOTDATA ] ( select_clause | '*' )
                      FROM table_name
                      [ WHERE where_clause ]
                      [ GROUP BY group_by_clause ]
                      [ ORDER BY ordering_clause ]
                      [ PER PARTITION LIMIT (integer | bind_marker) ]
                      [ LIMIT (integer | bind_marker) ]
                      [ ALLOW FILTERING ]
select_clause    ::=  selector [ AS identifier ] ( ',' selector [ AS identifier ] )
selector         ::=  column_name
                      | term
                      | CAST '(' selector AS cql_type ')'
                      | function_name '(' [ selector ( ',' selector )* ] ')'
                      | COUNT '(' '*' ')'
where_clause     ::=  relation ( AND relation )*
relation         ::=  column_name operator term
                      '(' column_name ( ',' column_name )* ')' operator tuple_literal
                      TOKEN '(' column_name ( ',' column_name )* ')' operator term
operator         ::=  '=' | '<' | '>' | '<=' | '>=' | '!=' | IN | CONTAINS | CONTAINS KEY
group_by_clause  ::=  column_name ( ',' column_name )*
ordering_clause  ::=  column_name [ ASC | DESC ] ( ',' column_name [ ASC | DESC ] )*
说明
  • SELECT语句读取表中一个或多个行的一个或多个列。它返回与请求匹配的行的结果集,其中每一行都包含与查询相对应的选择值。另外,可以将包括聚合函数在内的函数应用于结果。

  • SELECT语句至少包含一个选择子句和该选择所在的表的名称。请注意,Lindorm CQL暂不支持Join或子查询,因此SELECT语句仅适用于单个表。在大多数情况下,SELECT还有WHERE子句,并且可以选择其他子句来对结果进行排序或限制。如果提供了ALLOW FILTERING语句,则可以允许需要过滤的查询。

示例

CREATE TABLE persioninfo  (c1 int primary key, c2 text, c3 text);
SELECT c1, c2 FROM persioninfo WHERE c1 IN (199, 200, 207);
SELECT JSON c1, c2 FROM persioninfo WHERE c1 = 199;
SELECT c1 AS name, c2 AS address FROM persioninfo;


CREATE TABLE persioninfo  (c1 int , c2 text, c3 text, PRIMARY KEY(c1,c2));
SELECT *
FROM persioninfo
WHERE c1 = 12
  AND c2 > '2011-02-03'
  AND c2 <= '2012-01-01';

SELECT COUNT (*) AS user_count FROM persioninfo;

SELECT的选择子句

选择子句确定需要查询哪些列并在结果集中返回,以及在返回之前对该结果进行对应的转换。它由逗号分隔的选择器(Selector)列表组成,或者由通配符(*)组成,以选择表中定义的所有列。包括以下内容:

  • Selectors

    包括以下类型:

    • column_name:查询的表的列名,以检索该列的值。

    • term:一个term,通常嵌套在其他选择器(如函数)中,如果直接选择一个术语,则结果集的对应列将仅具有返回的每一行的该术语的值。

    • CAST '(' selector AS cql_type ')':强制转换,允许将嵌套选择器转换为(兼容)类型。

    • function_name '(' [ selector ( ',' selector )* ] ')':函数调用,其中参数是选择器本身。

    • COUNT '(' '*' ')':对COUNT函数的特殊调用COUNT(*),对所有非空结果进行计数。

  • WRITETIME function

    Selection仅支持特殊函数(其他任何地方都不允许)WRITETIME,这个函数仅接受一个参数,并且该参数必须为列名。这个函数允许检索存储在每个列内部的元信息,即WRITETIME列的值的时间戳。

  • where clause

    where子句指定必须查询的行。它由作为主键的一部分(partition key 或者clustering columns),或在其上定义了二级索引的列的关系组成。并非所有关系查询都是允许的,例如,不支持partition key的非相等关系(其中IN被视为相等关系)。对于给定的partition keyclustering column引起行的排序,并且它们的关系被限制为允许选择连续的(对于排序)行集的关系。例如,创建如下表posts

    CREATE TABLE posts (
        userid text,
        blog_title text,
        posted_at timestamp,
        entry_title text,
        content text,
        category int,
        PRIMARY KEY (userid, blog_title, posted_at)
    );

    可以进行如下的查询:

     SELECT entry_title, content FROM posts
     WHERE userid = 'john doe'
       AND blog_title='John''s Blog'
       AND posted_at >= '2012-01-01' AND posted_at < '2012-01-31';

    但是不能进行如下的查询,因为它不会选择一组连续的行(并且我们假设未设置二级索引)。

    SELECT entry_title, content FROM posts
     WHERE userid = 'john doe'
       AND posted_at >= '2012-01-01' AND posted_at < '2012-01-31';

    此外,仅在partition key的最后一列和primary key的最后一列上允许IN关系,也可以使用元组符号将Clustering column组合在一起。如下:

    SELECT * FROM posts
     WHERE userid = 'john doe'
       AND (blog_title, posted_at) > ('John''s Blog', '2012-01-01');

    将按照聚类顺序要求所有排在“John''s Blog”为blog_title和“2012-01-01”为post_at之后的行。特别是,只要post_at <='2012-01-01'的行的blog_title>'John's Blog',则将返回该行,但以下情况除外:

    SELECT * FROM posts
     WHERE userid = 'john doe'
       AND blog_title > 'John''s Blog'
       AND posted_at > '2012-01-01';

    元组符号也可用于clustering column的IN子句:

    SELECT * FROM posts
     WHERE userid = 'john doe'
       AND (blog_title, posted_at) IN (('John''s Blog', '2012-01-01'), ('Extreme Chess', '2014-06-01'));
  • Grouping results

    GROUP BY选项允许将所有共享一组列的相同值的选定行浓缩为单个行。使用GROUP BY选项,只能在partition key级别或clustering column级别对行进行分组。因此,GROUP BY选项仅按主键顺序接受主键列名称作为参数。如果主键列受到相等性限制的约束,则它不需要出现在GROUP BY子句中。

    说明

    因为CQL的GROUP BY是把数据拉到一个Server节点进行处理,数据量大的情况下,整个过程比较耗时。

  • Ordering results

    ORDER BY子句允许选择返回结果的顺序。它以列名列表和列顺序作为参数(升序为ASC,降序为DESC,没有指定顺序与ASC等效)。当前,可能的顺序受表上定义的clustering order限制:

    • 如果表是在没有任何特定CLUSTERING ORDER的情况下定义的,则允许的排序是由clustering columns和与之相反的顺序引起的。

    • 否则,允许的顺序是CLUSTERING ORDER选项的顺序和相反的顺序。

  • Limiting results

    SELECT语句的LIMIT选项限制查询返回的行数。

  • Allowing filtering

    默认情况下,Lindorm CQL仅允许不涉及“filtering”服务器端的数据的选择查询,即我们知道所有(实时的)记录读取将在结果集中返回(或部分返回)。原因是这些“non filtering”查询在某种意义上可以具有可预测的性能,因为它们可以在与查询返回的数据量成一定比例的时间内执行完成(可以通过LIMIT进行控制)。

    ALLOW FILTERING选项允许显式允许(某些)需要过滤的查询。请注意,使用ALLOW FILTERING的查询可能造成服务端出现不可预测的性能问题,即使选择少数记录的查询也可能表现,这取决于集群中存储的数据总量。

    例如,创建的如下表格,其中包含用户个人资料及其出生年份(带有次级索引)和居住国家/地区:

    CREATE TABLE users (
        username text PRIMARY KEY,
        firstname text,
        lastname text,
        birth_year int,
        country text
    );
    
    CREATE INDEX ON users(birth_year);

    可以进行以下有效查询:

    SELECT * FROM users;
    SELECT * FROM users WHERE birth_year = 1981;

    因为在这两种情况下,Lindorm CQL都会保证这些查询的性能与返回的数据量成正比。特别是,如果没有用户出生于1981年,那么第二次查询的性能将不取决于存储在数据库中的用户配置文件的数量(至少不是直接地:由于二级索引实现的考虑,该查询可能仍取决于集群中节点的数量,以及间接取决于集群存储的数据量。当然,在实践中,两个查询都可能返回非常大的结果集,但是始终可以通过添加LIMIT来控制返回的数据量。以下的查询将被拒绝:

    SELECT * FROM users WHERE birth_year = 1981 AND country = 'FR';

    因为Lindorm CQL无法保证即使在查询的结果很小的情况下,也不去进行大量数据的扫描。通常,它将扫描所有1981年出生的用户的索引条目,即使实际上只有少数来自法国。但是,如果您“知道自己在做什么”,则可以使用ALLOW FILTERING强制执行此查询,因此以下查询有效:

    SELECT * FROM users WHERE birth_year = 1981 AND country = 'FR' ALLOW FILTERING;
  • HOTDATA

    在用户建表指定冷热分离的场景下,如果用户想要查询热数据,可以在SELECT语句关键字下指定HOTDATA:

    SELECT HOTDATA * FROM persioninfo;

    默认不指定的话,请求会在冷热数据都进行查询,然后合并出所需的数据。

  • SEARCH INDEX SELECT操作

    对构建SEARCH INDEX的列支持模糊匹配查询,以及多维组合查询以及排序功能。

    • 常见模糊匹配

      构建SEARCH INDEX的列,在进行select操作的时候,支持对构建索引的列进行模糊匹配查询,模糊匹配使用%表示0到任意个字符匹配,常见的模糊匹配支持前缀匹配、后缀匹配、前后缀匹配、完全匹配。示例如下:

      SELECT * FROM persioninfo WHERE c2 LIKE 'v2';//完全匹配,c2列数字为v2。
      SELECT * FROM kss.tb WHERE c2 LIKE '%v2'; //前缀匹配,表示v2关键字符前支持0到任意个字符匹配。
      SELECT * FROM kss.tb WHERE c2 LIKE 'v2%' ;//后缀匹配,表示v2关键字符后支持0到任意个字符匹配。
      SELECT * FROM kss.tb WHERE c2 LIKE '%v2%';//contains匹配,表示v2关键字符前后支持任意个字符匹配。
    • 指定search_query查询

      使用search_query关键字查询,支持在select 语句中支持任意类solr原生query的语句,满足更丰富的查询检索需求。示例如下:

      SELECT selectors
        FROM table
        WHERE (indexed_column_expression | search_query = 'search_expression') 
        [ LIMIT n ]
        [ ORDER BY column_name ] ;

      Select语句中search_query关键字后面会跟随search_expression,具体的search_query的跟随字符限制请参考跟随符限制。常见的search_query跟随的选项支持情况如下:

      {
        "q": query_expression (string), 
        "fq": filter_query_expression(s) (string_or_array_of_strings, ...),  
        "facet": facet_query_expression (object) 
        "sort": sort_expression (string), 
        "start": start_index(number),
        timeAllowed: search_time_limit_ms,  
        "TZ": zoneID),   //  Any valid zone ID in java TimeZone class                            
        "paging": "driver" (string),
        "distrib.singlePass": true|false (boolean),
        "shards.failover": true|false (boolean),    // Default: true
        "shards.tolerant": true|false (boolean),    // Default: false
        "commit": true|false (boolean),
        "route.partition": partition_routing_expression (array_of_strings), 
        "route.range": range_routing_expression (array_of_strings), 
        "query.name": query_name (string),
      }

      示例如下:

      CREATE TABLE persioninfo (name text PRIMARY KEY, city text,id int) WITH extensions = {'CONSISTENCY_TYPE':'strong', 'MUTABILITY':'IMMUTABLE'};
      CREATE SEARCH INDEX sidx on persioninfo WITH COLUMNS (id,city);
      INSERT INTO persioninfo (name,city, id) VALUES ('MY','hangzhou', 18);
      SELECT name,id FROM persioninfo  WHERE search_query = 'city:hangzhou';
      说明

      因为search_query支持的语义比较丰富,所以暂时不支持searc_query语句与其他condition的组合使用,比如select * from table where search_query = 'c1:name' And column = 'a',这种用法是不支持的。

UPDATE

更新数据。

语法

update_statement ::=  UPDATE table_name
                      [ USING update_parameter ( AND update_parameter )* ]
                      SET assignment ( ',' assignment )*
                      WHERE where_clause
update_parameter ::=  ( integer | bind_marker )
assignment       ::=  simple_selection '=' term
                     | column_name '=' column_name ( '+' | '-' ) term
                     | column_name '=' list_literal '+' column_name
simple_selection ::=  column_name
                     | column_name '[' term ']'
                     | column_name '.' `field_name
condition        ::=  simple_selection operator term
说明
  • UPDATE语句为表指定的行更新一个或多个列。where_clause用于选择要更新的行,并且必须包括组成PRIMARY KEY的所有列。然后使用SET关键字设置非主键列。

  • 与SQL不同,UPDATE默认情况下不检查该行之前是否存在:如果该行之前不存在,则创建该行,否则进行更新。所以,没有办法知道是否发生了创建或更新。Lindorm CQL中的counter列允许与非counter列共存,这点是Lindorm CQL与Apache Cassandra CQL的一个区别。

  • 在UPDATE语句中,同一分区键(partition key)中的所有更新都是atomically以及isolation

  • 关于assignment,c=c + 3用于递增/递减计数器。 “ =”符号后的列名称必须与“ =”符号前的列名称相同。请注意,递增/递减仅允许在计数器(counter)上进行,并且是计数器唯一允许的更新操作。

示例

CREATE TABLE product (productname text PRIMARY KEY, company text, subdepartment text);
UPDATE product 
   SET company   = 'aliyun cloud',
       subdepartment = 'database nosql'
 WHERE productname = 'lindorm';

DELETE

删除行或行的一部分。

语法

delete_statement ::=  DELETE [ simple_selection ( ',' simple_selection ) ]
                      FROM table_name
                      [ USING update_parameter ( AND update_parameter )* ]
                      WHERE where_clause
说明
  • DELETE语句删除列或者行。如果在DELETE关键字之后直接提供了列名,则仅从WHERE子句指示的行中删除那些列。否则将删除整个行。

  • WHERE子句指定要删除的行。使用IN运算符可以用一条语句删除多行。暂不支持删除一定范围的行。

  • DELETE语句中,同一分区键(Partition Key)中的所有删除操作都是以原子方式独立应用的。

示例

 INSERT INTO t1 (c1,c2,c3) VALUES ( '11', '22', '33');
 DELETE c2 FROM t1 WHERE c1 IN ( '11');