データ操作
このセクションでは、データを挿入、更新、削除、およびクエリするために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
ステートメントは、テーブル内の1つ以上の行の1つ以上の列を読み取ります。リクエストに一致する行の結果セットを返し、各行にはクエリに対応する選択の値が含まれています。さらに、集計関数を含む関数を結果に適用できます。
SELECT
ステートメントには、少なくとも選択句と、選択が実行されるテーブル名が含まれています。CQLは**結合**または**サブクエリ**を実行せず、selectステートメントは単一のテーブルにのみ適用されます。selectステートメントには、クエリ結果をさらに絞り込むことができる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
関数
選択は、他の場所では許可されていない3つの特別な関数WRITETIME
、MAXWRITETIME
、およびTTL
をサポートしています。すべての関数は、列名を1つの引数としてのみ受け取ります。列がコレクションまたはUDTの場合、WRITETTIME(phones[2..4])
やWRITETTIME(user.name)
などの要素セレクタを追加できます。これらの関数は、各列に対して内部的に保存されているメタ情報を取得します。
-
WRITETIME
は、列の値のタイムスタンプを格納します。 -
MAXWRITETIME
は、列の値の最大のタイムスタンプを格納します。非コレクションおよび非UDT列の場合、MAXWRITETIME
はWRITETIME
と同等です。その他の場合は、列内の値の最大のタイムスタンプを返します。 -
TTL
は、期限切れに設定されている場合、列の値の残りの存続時間(秒単位)を格納します。それ以外の場合はnull
になります。
WRITETIME
関数とTTL
関数は、非フリーズコレクションや非フリーズユーザー定義型などの複数セルの列で使用できます。その場合、これらの関数は、選択された各セルに対するタイムスタンプまたはTTLのリストを返します。
WHERE
句
WHERE
句は、どの行がクエリされるかを指定します。PRIMARY KEY
列またはセカンダリインデックスが定義されている列の関係と、一連の値を指定します。
クエリでは、すべてのリレーションシップが許可されるわけではありません。たとえば、パーティションキーでは等号のみが許可されます。IN
句は、1つ以上の値に対する等号と見なされます。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
オプションを使用すると、一連の列に対して同じ値を共有する選択されたすべての行を1行に圧縮できます。
GROUP BY
オプションを使用すると、パーティションキーまたはクラスタリング列レベルで行をグループ化できます。したがって、GROUP BY
オプションは、定義された順序でプライマリキー列のみを引数として受け入れます。プライマリキー列が等価制約によって制限されている場合、GROUP BY
句には含まれません。
集計関数は、各グループに対して個別の値を生成します。GROUP BY
句が指定されていない場合、集計関数はすべての行に対して単一の値を生成します。
集約関数を用いない状態で、GROUP BY
句を含むステートメントで列が選択されると、各グループで最初に遭遇した値が返されます。
結果の順序付け
ORDER BY
句は、返される結果の順序を選択します。引数は列名のリストであり、各列の順序(昇順の場合はASC
、降順の場合はDESC
)を指定します。可能な順序付けは、テーブルに定義されているクラスタリング順序によって制限されます。
-
テーブルが特定の
CLUSTERING ORDER
なしで定義されている場合、順序はクラスタリング列によって定義された順序またはその逆順になります。 -
それ以外の場合は、順序は
CLUSTERING ORDER
オプションと、その逆順によって定義されます。
結果の制限
SELECT
ステートメントのLIMIT
オプションは、クエリによって返される行数を制限します。PER PARTITION LIMIT
オプションは、クエリによって指定されたパーティションごとに返される行数を制限します。両方の種類の制限を同じステートメントで使用できます。
フィルタリングの許可
デフォルトでは、CQLはすべてのパーティションのフルスキャンを含まないselectクエリのみを許可します。すべてのパーティションがスキャンされる場合、結果を返すのに、テーブル内のデータ量に比例した大きなレイテンシが発生する可能性があります。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;
どちらの場合も、クエリの性能は返されるデータ量に比例します。最初のクエリは、すべてのユーザーが選択されるため、すべての行を返します。2番目のクエリは、セカンダリインデックスによって定義された行のみを返します。これはノードごとの実装であり、結果はクラスタ内のノード数に依存し、間接的に格納されているデータ量に反比例します。ノード数は、常に格納されているユーザープロファイルの数よりも桁違いに少なくなります。どちらのクエリも非常に大きな結果セットを返す可能性がありますが、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
ステートメントは、テーブル内の特定の行に対して1つ以上の列を書き込みます。行はPRIMARY KEY
によって識別されるため、少なくとも1つの列を指定する必要があります。挿入する列のリストは、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
ステートメントは、テーブル内の特定の行に対して1つ以上の列を書き込みます。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を削除します。TTLnull
は、TTL 0で挿入することと同じです。
UPDATE
、INSERT
、DELETE
、およびBATCH
ステートメントは、次のパラメータをサポートします。
-
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
キーワードの直後に列名が指定されている場合、WHERE
句で示された行からそれらの列のみが削除されます。それ以外の場合は、行全体が削除されます。
WHERE
句は、削除する行を指定します。IN
演算子を使用することで、1つのステートメントで複数の行を削除できます。不等号演算子(例:>=
)を使用して、行の範囲を削除できます。
DELETE
は、更新と同じセマンティクスでTIMESTAMP
オプションをサポートします。
DELETE
ステートメントでは、同じパーティションキー内のすべての削除は、アトミックかつ独立して適用されます。
DELETE
操作は、UPDATE
およびINSERT
ステートメントと同様に、IF
句を使用して条件付きにすることができます。ただし、INSERT
およびUPDATE
ステートメントと同様に、Paxosが使用されるため、無視できないパフォーマンスコストが発生し、控えめに使用する必要があります。
BATCH
複数の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
ステートメントにリストされている順序とは異なる順序で適用される場合があります。特定の操作順序を強制するには、操作ごとのタイムスタンプを指定する必要があります。 -
単一パーティションへのログ付きバッチは、最適化としてログなしのバッチに変換されます。