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 ステートメントは、テーブル内の1つ以上の行の1つ以上の列を読み取ります。リクエストに一致する行の結果セットを返し、各行にはクエリに対応する選択の値が含まれています。さらに、集計関数を含む関数を結果に適用できます。

SELECT ステートメントには、少なくとも選択句と、選択が実行されるテーブル名が含まれています。CQLは**結合**または**サブクエリ**を実行せず、selectステートメントは単一のテーブルにのみ適用されます。selectステートメントには、クエリ結果をさらに絞り込むことができるwhere句を含めることもできます。追加の句では、結果を順序付けまたは制限できます。最後に、クラスタ全体のフィルタリングを必要とするクエリは、任意のクエリにALLOW FILTERINGを追加できます。CASSANDRA-18238以降の仮想テーブルでは、クエリで通常必要となる場合にALLOW FILTERINGを指定する必要はありません。詳細については、仮想テーブルのドキュメントを参照してください。

選択句

select_clauseは、結果セットでクエリされ、返される列を決定します。この句は、返す前に結果に適用する変換も適用できます。選択句は、コンマ区切りの特定のセレクタのリスト、または、テーブルに定義されているすべての列を選択するワイルドカード文字(*)で構成されます。

セレクタ

selectorは次のいずれかになります。

  • 選択されたテーブルの列名で、その列の値を取得します。

  • 関数などの他のセレクタの中にネストして使用される用語です(用語が直接選択された場合、結果セットの対応する列には、返されるすべての行に対してこの用語の値が単純に含まれます)。

  • ネストされたセレクタを(互換性のある)型に変換できるキャストです。

  • 引数がそれ自体セレクタである関数呼び出しです。詳細は、関数に関するセクションを参照してください。

  • すべての非NULLの結果をカウントする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

現在、エイリアスはステートメントのWHERE句またはORDER BY句では認識されません。代わりに元の列名を使用する必要があります。

WRITETIMEMAXWRITETIME、およびTTL関数

選択は、他の場所では許可されていない3つの特別な関数WRITETIMEMAXWRITETIME、およびTTLをサポートしています。すべての関数は、列名を1つの引数としてのみ受け取ります。列がコレクションまたはUDTの場合、WRITETTIME(phones[2..4])WRITETTIME(user.name)などの要素セレクタを追加できます。これらの関数は、各列に対して内部的に保存されているメタ情報を取得します。

  • WRITETIMEは、列の値のタイムスタンプを格納します。

  • MAXWRITETIMEは、列の値の最大のタイムスタンプを格納します。非コレクションおよび非UDT列の場合、MAXWRITETIMEWRITETIMEと同等です。その他の場合は、列内の値の最大のタイムスタンプを返します。

  • 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のトークンに基づいて選択されます。

キーのトークンは使用中のパーティショナに依存し、特に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オプションを使用すると、一連の列に対して同じ値を共有する選択されたすべての行を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代入について

  • c = c + 3は、カウンターを増減します。これは許可される唯一の操作です。`=`記号の後の列名は、`=`記号の前にある列名と**必ず**同じでなければなりません。増減は、カウンターでのみ許可されます。詳細はカウンターのセクションを参照してください。

  • id = id + <some-collection>id[value1] = value2はコレクション用です。詳細はコレクションを参照してください。

  • id.field = 3は、フリーズされていないユーザー定義型のフィールドの値を設定するためです。詳細はUDTを参照してください。

更新パラメータ

UPDATEおよびINSERTステートメントは、次のパラメータをサポートします。

  • TTL:挿入された値の有効期限(秒単位)をオプションで指定します。設定されている場合、挿入された値は指定された時間後にデータベースから自動的に削除されます。TTLは列自体ではなく、挿入された値に関することに注意してください。これは、列のその後の更新でもTTL(その更新で指定されたTTL)がリセットされることを意味します。デフォルトでは、値は期限切れになりません。TTL 0はTTLなしと同じです。テーブルにdefault_time_to_liveがある場合、TTL 0は挿入または更新された値のTTLを削除します。TTL nullは、TTL 0で挿入することと同じです。

UPDATEINSERTDELETE、および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

複数のINSERTUPDATE、および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ステートメントには、UPDATEINSERT、およびDELETEステートメントのみを含めることができます(たとえば、他のバッチは含めません)。

  • バッチは、SQLトランザクションの完全なアナログではありません。

  • 各操作にタイムスタンプが指定されていない場合、すべての操作は同じタイムスタンプ(自動的に生成されたもの、またはバッチレベルで提供されたタイムスタンプ)で適用されます。タイムスタンプのタイの場合のCassandraの競合解決手順のため、操作はBATCHステートメントにリストされている順序とは異なる順序で適用される場合があります。特定の操作順序を強制するには、操作ごとのタイムスタンプを指定する必要があります。

  • 単一パーティションへのログ付きバッチは、最適化としてログなしのバッチに変換されます。

ログなしのバッチ

デフォルトでは、Cassandraはバッチログを使用して、バッチ内のすべての操作が最終的に完了するか、どれも完了しないように保証します(ただし、操作は単一パーティション内でのみ分離されることに注意してください)。

バッチが複数のパーティションにまたがる場合、バッチの原子性にはパフォーマンス上のペナルティがあります。このペナルティを発生させたくない場合は、UNLOGGEDオプションを使用して、Cassandraにバッチログをスキップするように指示できます。UNLOGGEDオプションを使用すると、バッチが失敗した場合、パッチが部分的にしか適用されない可能性があります。

カウンターバッチ

バッチ処理されたカウンターの更新には、COUNTERオプションを使用します。Cassandraの他の更新とは異なり、カウンターの更新はべき等ではありません。