Cassandra 文档

版本

您正在查看预发布版本的文档。

数据操作

本节介绍 CQL 支持的用于插入、更新、删除和查询数据的语句。

SELECT

使用 SELECT 语句从数据中查询数据

select_statement::= SELECT [ JSON | DISTINCT ] ( 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 name, occupation FROM users WHERE userid IN (199, 200, 207);
SELECT JSON name, occupation FROM users WHERE userid = 199;
SELECT name AS user_name, occupation AS user_occupation FROM users;

SELECT time, value
FROM events
WHERE event_type = 'myEvent'
  AND time > '2011-02-03'
  AND time <= '2012-01-01'

SELECT COUNT (*) AS user_count FROM users;

SELECT 语句读取表中一个或多个行的一个或多个列。它返回与请求匹配的行的结果集,其中每行包含与查询相对应的选择的列值。此外,可以将函数(包括聚合)应用于结果。

SELECT 语句至少包含一个选择子句和执行选择的表的名称。CQL 执行联接或子查询,并且选择语句仅应用于单个表。选择语句还可以包含一个where 子句,该子句可以进一步缩小查询结果。其他子句可以排序限制结果。最后,需要完整集群过滤的查询可以在任何查询中追加 ALLOW FILTERING。对于虚拟表,从CASSANDRA-18238开始,当查询通常需要 ALLOW FILTERING 时,无需指定它。请参阅虚拟表的文档以了解更多信息。

选择子句

select_clause 确定将在结果集中查询和返回哪些列。此子句还可以应用转换以在返回之前应用于结果。选择子句由一个逗号分隔的特定选择器列表组成,或者,可以使用通配符字符 (*) 来选择表中定义的所有列。

选择器

选择器 可以是以下之一

  • 所选表的列名,用于检索该列的值。

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

  • 一个强制转换,它允许将嵌套选择器转换为(兼容)类型。

  • 一个函数调用,其中参数本身是选择器。有关更多详细信息,请参阅有关函数的部分。

  • COUNT 函数的特殊调用 COUNT(*),它计算所有非空结果。

别名

每个顶层选择器也可以使用 AS 赋予别名。如果是这样,结果集中相应列的名称将是别名的名称。例如

// Without alias
SELECT int_as_blob(4) FROM t;

//  int_as_blob(4)
// ----------------
//  0x00000004

// With alias
SELECT int_as_blob(4) AS four FROM t;

//  four
// ------------
//  0x00000004

目前,别名在语句的 WHEREORDER BY 子句中不被识别。您必须使用原始列名。

WRITETIMEMAXWRITETIMETTL 函数

选择支持三个在其他地方不允许的特殊函数:WRITETIMEMAXWRITETIMETTL。所有函数都只接受一个参数,即列名。如果列是集合或 UDT,则可以添加元素选择器,例如 WRITETTIME(phones[2..4])WRITETTIME(user.name)。这些函数检索为每个列内部存储的元信息

  • WRITETIME 存储列值的 timestamp。

  • MAXWRITETIME 存储列值的 largest timestamp。对于非集合和非 UDT 列,MAXWRITETIME 等效于 WRITETIME。在其他情况下,它返回列中值的 largest timestamp。

  • TTL 存储列值的剩余生存时间(以秒为单位),如果它被设置为过期;否则值为 null

WRITETIMETTL 函数可用于多单元格列,例如非冻结集合或非冻结用户定义类型。在这种情况下,这些函数将返回每个选定单元格的 timestamp 或 TTL 列表。

WHERE 子句

WHERE 子句指定查询哪些行。它为 PRIMARY KEY 列或已定义二级索引的列指定一个关系,以及一个设置值。

查询中并非所有关系都是允许的。例如,分区键上只允许相等性。IN 子句被认为是一个或多个值的相等性。TOKEN 子句可用于查询分区键非相等性。分区键必须在 WHERE 子句中指定在聚簇列之前。聚簇列的关系必须指定一个连续的行集以进行排序。

例如,给定

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';

但以下查询是不允许的,因为它没有选择一个连续的行集(我们假设没有设置二级索引)

// Needs a blog_title to be set to select ranges of posted_at

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

在指定关系时,可以将 TOKEN 函数应用于 PARTITION KEY 列以进行查询。将根据 PARTITION_KEY 的 token 选择行,而不是根据值。

键的 token 取决于使用的分区器,特别是 RandomPartitioner 不会产生有意义的顺序。还要注意,排序分区器始终按字节对 token 值进行排序(因此,即使分区键是 int 类型,token(-1) > token(0) 也是如此)。

例如

SELECT * FROM posts
 WHERE token(userid) > token('tom') AND token(userid) < token('bob');

IN 关系仅允许在分区键的最后一列或完整主键的最后一列上使用。

还可以使用元组表示法将 CLUSTERING COLUMNS “分组”到一个关系中。

例如

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

此查询将返回所有在聚簇排序中具有 “John’s Blog” 作为 blog_tile 和 '2012-01-01' 作为 posted_at 的行之后的行。特别是,只要它们的 blog_title > 'John''s Blog',具有 post_at ⇐ '2012-01-01' 的行将被返回。

对于以下示例,情况并非如此

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

元组表示法也可用于聚簇列上的 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'));

CONTAINS 运算符只能用于集合列(列表、集合和映射)。在映射的情况下,CONTAINS 应用于映射值。CONTAINS KEY 运算符只能用于映射列,并应用于映射键。

分组结果

GROUP BY 选项可以将共享一组列的相同值的选定所有行压缩成一行。

使用 GROUP BY 选项,可以按分区键或聚簇列级别对行进行分组。因此,GROUP BY 选项仅接受按定义顺序排列的主键列作为参数。如果主键列受到相等性限制,则不将其包含在 GROUP BY 子句中。

聚合函数将为每个组生成一个单独的值。如果没有指定 GROUP BY 子句,聚合函数将为所有行生成一个单一值。

如果在包含 GROUP BY 的语句中选择一个列而没有聚合函数,则将返回每个组中遇到的第一个值。

排序结果

ORDER BY 子句选择返回结果的顺序。参数是一个列名列表,以及每列的顺序(ASC 表示升序,DESC 表示降序,可能的排序受表上定义的聚簇排序限制

  • 如果表已定义为没有任何特定的 CLUSTERING ORDER,则顺序按聚簇列定义,或反之

  • 否则,顺序由 CLUSTERING ORDER 选项及其反向定义。

限制结果

SELECT 语句的 LIMIT 选项限制查询返回的行数。PER PARTITION LIMIT 选项限制查询为给定分区返回的行数。两种类型的限制都可以在同一个语句中使用。

允许过滤

默认情况下,CQL 仅允许不涉及对所有分区的完整扫描的选择查询。如果扫描了所有分区,则返回结果可能会遇到与表中数据量成正比的显着延迟。ALLOW FILTERING 选项显式执行完整扫描。因此,查询的性能可能不可预测。

例如,考虑以下包含用户资料、出生年份和居住国的表格。出生年份定义了二级索引。

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

CREATE INDEX ON users(birth_year);

以下查询有效

// All users are returned
SELECT * FROM users;

// All users with a particular birth year are returned
SELECT * FROM users WHERE birth_year = 1981;

在这两种情况下,查询性能与返回的数据量成正比。第一个查询返回所有行,因为选择了所有用户。第二个查询仅返回由二级索引定义的行,这是一个每个节点的实现;结果将取决于集群中的节点数量,并且与存储的数据量成反比。节点数量始终比存储的用户资料数量低几个数量级。这两个查询都可能返回非常大的结果集,但添加 LIMIT 子句可以减少延迟。

以下查询将被拒绝

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

Cassandra 无法保证即使结果很小,也不会扫描大量数据。如果您知道数据集很小,并且性能将是合理的,请添加 ALLOW FILTERING 以允许查询执行

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

INSERT

使用 INSERT 语句插入行的數據。

insert_statement::= INSERT INTO table_name ( names_values | json_clause )
	[ IF NOT EXISTS ]
	[ USING update_parameter ( AND update_parameter )* ]
names_values::= names VALUES tuple_literal
json_clause::= JSON string [ DEFAULT ( NULL | UNSET ) ]
names::= '(' column_name ( ',' column_name )* ')'

例如

INSERT INTO NerdMovies (movie, director, main_actor, year)
   VALUES ('Serenity', 'Joss Whedon', 'Nathan Fillion', 2005)
   USING TTL 86400;

INSERT INTO NerdMovies JSON '{"movie": "Serenity", "director": "Joss Whedon", "year": 2005}';

INSERT 语句为表中给定行的一个或多个列写入数据。由于行由其 PRIMARY KEY 标识,因此必须至少指定一个列。要插入的列列表必须使用 VALUES 语法提供。使用 JSON 语法时,VALUES 是可选的。有关更多详细信息,请参阅有关 JSON 支持 的部分。INSERT 的所有更新都是原子地且隔离地应用的。

与 SQL 不同,INSERT 默认情况下不会检查行的先前存在。如果之前不存在,则创建该行,否则更新该行。此外,没有办法知道发生了哪种操作。

IF NOT EXISTS 条件可以在行不存在的情况下限制插入。但是,请注意,使用 IF NOT EXISTS 会产生不可忽略的性能成本,因为使用了 Paxos,因此应谨慎使用。

有关 update_parameter 的信息,请参阅 UPDATE 部分。另请注意,INSERT 不支持计数器,而 UPDATE 支持。

UPDATE

使用 UPDATE 语句更新行。

update_statement ::=    UPDATE table_name
                        [ USING update_parameter ( AND update_parameter )* ]
                        SET assignment( ',' assignment )*
                        WHERE where_clause
                        [ IF ( EXISTS | condition ( AND condition)*) ]
update_parameter ::= ( TIMESTAMP | TTL ) ( 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 NerdMovies USING TTL 400
   SET director   = 'Joss Whedon',
       main_actor = 'Nathan Fillion',
       year       = 2005
 WHERE movie = 'Serenity';

UPDATE UserActions
   SET total = total + 2
   WHERE user = B70DE1D0-9908-4AE3-BE34-5573E5B09F14
     AND action = 'click';

UPDATE 语句为表中给定行的一个或多个列写入数据。WHERE 子句用于选择要更新的行,并且必须包含 PRIMARY KEY 的所有列。非主键列使用 SET 关键字设置。在 UPDATE 语句中,同一分区键内的所有更新都是原子地且隔离地应用的。

与 SQL 不同,UPDATE 默认情况下不会检查行的先前存在。如果之前不存在,则创建该行,否则更新该行。此外,没有办法知道发生了哪种操作。

IF 条件可用于在满足特定条件时选择是否更新行。但是,与 IF NOT EXISTS 条件一样,可能会产生不可忽略的性能成本。

关于 SET 赋值

  • c = c + 3 将增加/减少计数器,这是唯一允许的操作。等号后面的列名**必须**与等号前面的列名相同。仅允许对计数器进行增加/减少。有关详细信息,请参阅有关 计数器 的部分。

  • id = id + <some-collection>id[value1] = value2 用于集合。有关详细信息,请参阅 集合

  • id.field = 3 用于设置非冻结用户定义类型上字段的值。有关详细信息,请参阅 UDT

更新参数

UPDATEINSERT 语句支持以下参数

  • TTL:指定插入值的可选生存时间(以秒为单位)。如果设置,插入的值将在指定时间后自动从数据库中删除。请注意,TTL 涉及插入的值,而不是列本身。这意味着对列的任何后续更新也将重置 TTL(到该更新中指定的任何 TTL)。默认情况下,值永远不会过期。TTL 为 0 等效于没有 TTL。如果表具有 default_time_to_live,则 TTL 为 0 将删除插入或更新值的 TTL。TTL 为 null 等效于插入 TTL 为 0 的值。

UPDATEINSERTDELETEBATCH 语句支持以下参数

  • TIMESTAMP:设置操作的时间戳。如果未指定,协调器将在语句执行开始时使用当前时间(以微秒为单位)作为时间戳。这通常是一个合适的默认值。

DELETE

删除行或行的一部分使用 DELETE 语句。

delete_statement::= DELETE [ simple_selection ( ',' simple_selection ) ]
	FROM table_name
	[ USING update_parameter ( AND update_parameter# )* ]
	WHERE where_clause
	[ IF ( EXISTS | condition ( AND condition)*) ]

例如

DELETE FROM NerdMovies USING TIMESTAMP 1240003134
 WHERE movie = 'Serenity';

DELETE phone FROM Users
 WHERE userid IN (C73DE1D3-AF08-40F3-B124-3FF3E5109F22, B70DE1D0-9908-4AE3-BE34-5573E5B09F14);

DELETE 语句删除列和行。如果在 DELETE 关键字之后直接提供列名,则仅从 WHERE 子句指示的行中删除这些列。否则,将删除整个行。

WHERE 子句指定要删除哪些行。可以使用 IN 运算符删除多行。可以使用不等式运算符(如 >=)删除行范围。

DELETE 支持 TIMESTAMP 选项,其语义与 更新 中的语义相同。

DELETE 语句中,同一分区键内的所有删除都是原子地且隔离地应用的。

DELETE 操作可以通过使用 IF 子句来实现条件,类似于 UPDATEINSERT 语句。但是,与 INSERTUPDATE 语句一样,这会产生不可忽略的性能成本,因为使用了 Paxos,因此应谨慎使用。

BATCH

通过 BATCH 语句将多个 INSERTUPDATEDELETE 分组,可以在单个语句中执行。

batch_statement ::=     BEGIN [ UNLOGGED | COUNTER ] BATCH
                        [ USING update_parameter( AND update_parameter)* ]
                        modification_statement ( ';' modification_statement )*
                        APPLY BATCH
modification_statement ::= insert_statement | update_statement | delete_statement

例如

BEGIN BATCH
   INSERT INTO users (userid, password, name) VALUES ('user2', 'ch@ngem3b', 'second user');
   UPDATE users SET password = 'ps22dhds' WHERE userid = 'user3';
   INSERT INTO users (userid, password) VALUES ('user4', 'ch@ngem3c');
   DELETE name FROM users WHERE userid = 'user1';
APPLY BATCH;

BATCH 语句将多个修改语句(插入/更新和删除)分组到单个语句中。它有几个目的

  • 在对多个更新进行批处理时,它可以节省客户端和服务器之间(以及有时服务器协调器和副本之间)的网络往返次数。

  • 属于给定分区键的 BATCH 中的所有更新都以隔离方式执行。

  • 默认情况下,批次中的所有操作都以日志方式执行,以确保所有变异最终完成(或者没有一个会完成)。有关更多详细信息,请参阅有关 UNLOGGED 批次 的说明。

请注意

  • BATCH 语句只能包含 UPDATEINSERTDELETE 语句(例如,不能包含其他批次)。

  • 批次不是 SQL 事务的完全模拟。

  • 如果未为每个操作指定时间戳,则所有操作都将使用相同的时间戳应用(要么是自动生成的时间戳,要么是批次级别提供的时间戳)。由于 Cassandra 在 时间戳绑定 情况下冲突解决过程,操作可能以与它们在 BATCH 语句中列出的顺序不同的顺序应用。要强制执行特定的操作顺序,您必须指定每个操作的时间戳。

  • 对单个分区的 LOGGED 批次将作为优化转换为 UNLOGGED 批次。

UNLOGGED 批次

默认情况下,Cassandra 使用批次日志来确保批次中的所有操作最终完成或没有一个会完成(但请注意,操作仅在单个分区内隔离)。

当批次跨越多个分区时,批次原子性会产生性能损失。如果您不想承担这种损失,您可以使用 UNLOGGED 选项告诉 Cassandra 跳过批次日志。如果使用 UNLOGGED 选项,则失败的批次可能会导致补丁仅部分应用。

COUNTER 批次

对批处理计数器更新使用 COUNTER 选项。与 Cassandra 中的其他更新不同,计数器更新不是幂等的。