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 決定哪些欄位將在結果集中查詢並傳回。此子句也可以套用轉換,在傳回前套用至結果。選取子句包含特定選取器的逗號分隔清單,或者,使用萬用字元 (*) 選取表格中定義的所有欄位。

選取器

selector 可以是下列之一

  • 所選取的資料表欄位名稱,以擷取該欄位的數值。

  • 一個術語,通常用於巢狀嵌在其他選擇器內部,例如函式(如果直接選取一個術語,則結果集的對應欄位只會針對每一個傳回的列包含這個術語的數值)。

  • 一個轉換,允許將巢狀選擇器轉換為(相容的)類型。

  • 一個函式呼叫,其中參數本身就是選擇器。請參閱 函式 區段以取得更多詳細資料。

  • COUNT 函式 的特殊呼叫 COUNT(*),用來計算所有非 Null 結果。

別名

每個頂層選擇器也可以使用別名(使用 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 會儲存欄位數值最大的 Timestamp。對於非集合和非 UDT 欄位,MAXWRITETIME 等於 WRITETIME。在其他情況下,它會傳回欄位中數值最大的 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 的代碼而非值選取。

金鑰的代碼取決於使用的分割器,特別是 RandomPartitioner 產生的結果沒有意義的順序。另請注意,排序分割器總是按位元組對代碼值進行排序(因此,即使分割鍵的類型為 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');

此查詢將傳回所有在分群順序中,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 選項及其反向定義。

限制結果

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_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 指定

  • 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_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,因此應謹慎使用。

批次

多個 INSERTUPDATEDELETE 可以透過 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 語句只能包含 UPDATEINSERTDELETE 語句 (例如,不能包含其他批次)。

  • 批次不是 SQL 交易的完整類比。

  • 如果未為每個操作指定時間戳,則所有操作都將使用相同時間戳套用 (自動產生一個時間戳,或在批次層級提供的時間戳)。由於 Cassandra 在 時間戳平手 的情況下的衝突解決程序,操作可能會以與 BATCH 語句中列出的順序不同的順序套用。若要強制執行特定操作順序,您必須指定每個操作的時間戳。

  • 對單一分區的已記錄批次會轉換為未記錄批次作為最佳化。

UNLOGGED 批次

預設情況下,Cassandra 會使用批次記錄檔來確保批次中的所有作業最終完成,否則都不會完成(但請注意,作業只會在單一分割區中隔離)。

當批次跨越多個分割區時,批次原子性會產生效能損失。如果您不希望產生這種損失,您可以使用 UNLOGGED 選項告訴 Cassandra 略過批次記錄檔。如果使用 UNLOGGED 選項,失敗的批次可能會讓修補程式只部分套用。

COUNTER 批次

對批次計數器更新使用 COUNTER 選項。與 Cassandra 中的其他更新不同,計數器更新並非冪等。