資料處理
本節說明 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
陳述式至少包含一個選取子句和執行選取的表格名稱。CQL 不執行聯結或子查詢,且選取陳述式僅適用於單一表格。選取陳述式也可以有一個where 子句,進一步縮小查詢結果。其他子句可以排序或限制結果。最後,需要完整叢集篩選的查詢可以將 ALLOW FILTERING
附加到任何查詢。對於虛擬表格,從 CASSANDRA-18238 開始,當查詢通常需要時,不需要指定 ALLOW FILTERING
。請參閱虛擬表格的說明文件以了解更多資訊。
選取子句
select_clause
決定哪些欄位將在結果集中查詢並傳回。此子句也可以套用轉換,在傳回前套用至結果。選取子句包含特定選取器的逗號分隔清單,或者,使用萬用字元 (*
) 選取表格中定義的所有欄位。
別名
每個頂層選擇器也可以使用別名(使用 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
目前,在陳述式的 |
WRITETIME
、MAXWRITETIME
和 TTL
函式
選取支援三個特殊函式,這些函式在其他任何地方都不允許使用:WRITETIME
、MAXWRITETIME
和 TTL
。所有函式只接受一個參數,也就是欄位名稱。如果欄位是集合或 UDT,則可以加入元素選擇器,例如 WRITETTIME(phones[2..4])
或 WRITETTIME(user.name)
。這些函式會擷取針對每個欄位內部儲存的元資料
-
WRITETIME
會儲存欄位數值的 Timestamp。 -
MAXWRITETIME
會儲存欄位數值最大的 Timestamp。對於非集合和非 UDT 欄位,MAXWRITETIME
等於WRITETIME
。在其他情況下,它會傳回欄位中數值最大的 Timestamp。 -
TTL
會儲存欄位數值設定到期時間的剩餘時間(以秒為單位);否則,數值為null
。
WRITETIME
和 TTL
函式可以用於多儲存格欄位,例如非凍結集合或非凍結使用者定義類型。在這種情況下,這些函式會傳回每個選取儲存格的 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
的代碼而非值選取。
金鑰的代碼取決於使用的分割器,特別是 |
例如
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');
此查詢將傳回所有在分群順序中,blog_tile
為「John’s Blog」且 posted_at
為 '2012-01-01' 的列之後排序的列。特別是,將傳回 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';
元組表示法也可以用於分群欄位的 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
選項及其反向定義。
允許篩選
預設情況下,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_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 部分以取得有關 update_parameter
的資訊。另請注意,INSERT
不支援計數器,而 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
指定
更新參數
UPDATE
和 INSERT
陳述式支援下列參數
-
TTL
:指定插入數值的選用生存時間(以秒為單位)。如果設定,插入的數值會在指定時間後自動從資料庫中移除。請注意,TTL 與插入的數值有關,而不是與欄位本身有關。這表示欄位的任何後續更新也會重設 TTL(重設為該更新中指定的 TTL)。預設情況下,數值永不過期。TTL 為 0 等於沒有 TTL。如果表格有 default_time_to_live,則 TTL 為 0 會移除插入或更新數值的 TTL。TTL 為null
等於插入 TTL 為 0。
UPDATE
、INSERT
、DELETE
和 BATCH
陳述式支援下列參數
-
TIMESTAMP
:設定操作的時間戳。如果未指定,協調器會在語句執行開始時使用微秒為單位的目前時間作為時間戳。這通常是合適的預設值。
刪除
刪除列或列的一部分會使用 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
子句進行條件式,類似於 UPDATE
和 INSERT
語句。然而,與 INSERT
和 UPDATE
語句一樣,這會產生不可忽略的效能成本,因為會使用 Paxos,因此應謹慎使用。
批次
多個 INSERT
、UPDATE
和 DELETE
可以透過 BATCH
語句將它們分組在一起,在單一語句中執行
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
中的所有更新都會以隔離方式執行。 -
預設情況下,批次中的所有操作都會執行為已記錄,以確保所有突變最終完成 (或沒有任何突變會完成)。有關更多詳細資訊,請參閱 未記錄批次 的注意事項。
請注意
-
BATCH
語句只能包含UPDATE
、INSERT
和DELETE
語句 (例如,不能包含其他批次)。 -
批次不是 SQL 交易的完整類比。
-
如果未為每個操作指定時間戳,則所有操作都將使用相同時間戳套用 (自動產生一個時間戳,或在批次層級提供的時間戳)。由於 Cassandra 在 時間戳平手 的情況下的衝突解決程序,操作可能會以與
BATCH
語句中列出的順序不同的順序套用。若要強制執行特定操作順序,您必須指定每個操作的時間戳。 -
對單一分區的已記錄批次會轉換為未記錄批次作為最佳化。