Cassandra Query Language (CQL) v3.1.5

  1. Cassandra Query Language (CQL) v3
    1. CQL Syntax
      1. Preamble
      2. Conventions
      3. Identifiers and keywords
      4. Constants
      5. Comments
      6. Statements
      7. Prepared Statement
    2. Data Definition
      1. CREATE KEYSPACE
      2. USE
      3. ALTER KEYSPACE
      4. DROP KEYSPACE
      5. CREATE TABLE
      6. ALTER TABLE
      7. DROP TABLE
      8. TRUNCATE
      9. CREATE INDEX
      10. DROP INDEX
    3. Data Manipulation
      1. INSERT
      2. UPDATE
      3. DELETE
      4. BATCH
    4. Queries
      1. SELECT
    5. Data Types
      1. Working with dates
      2. Counters
      3. Working with collections
    6. Functions
      1. Token
      2. Uuid
      3. Timeuuid functions
      4. Blob conversion functions
    7. Appendix A: CQL Keywords
  2. Versioning

CQL Syntax

Preamble

このドキュメントはCassandra Query Language(CQL)のv3について書かれたものだ。CQL v3はCQL v2への後方互換を持っておらず、多くの点で異なっている。このドキュメントは最新のバージョンについて書かれている。changesセクションにおいて各バージョンでの変更点を記している。

CQL v3ではモデルがSQLにとても近いものになっているという印象を受けるだろう。データはテーブルにある行の列として保存されるといったことなど。それに合わせて、このドキュメントでは、テーブル、行、列のような用語はSQLでのそれらと同様の意味を持つ。だけど覚えておいてほしい。それらはCassandraの実装やthrift、CQL v2のAPIで見られた行や列のコンセプトとは別のものだ。

Conventions

CQLシンタックスを表現するため、このドキュメント中では表記に下記のルールを設ける:

<start> ::= TERMINAL <non-terminal1> <non-terminal1>
SELECT sample_usage FROM cql;

Identifiers and keywords

CQLはテーブルや列を識別するのに識別子(名前)を使う。識別子は正規表現[a-zA-Z0-9_]*にマッチする。

SELECTやWITHのような識別子はキーワードとする。それらは意味を与えられており、予約されている。キーワードのリストは付録Aで。

識別子や引用符で囲まれいないキーワードは大文字小文字の区識別子には引用識別子というものがある。これはダブルクオート(")で囲まれた文字列を指す。引用識別子はキーワードとしては使われない。囲われた文字列は大文字、小文字の違いが区別される。"My Id"と"my id"は違うものだ。すべて小文字の引用識別子は二重引用符を外せば、同じ文字で構成されたただの識別子と同じ意味となる("myid"はmyidとmyIdと同じ、"myId"とは違う)。引用識別子の中で二重引用符を使いたければ、二重引用符を二回打つことでエスケープされる("foo""bar")。

Constants

CQLでは次の定数を取ることができる: strings, integers, floats, booleans, uuids, blobs:

これらの定数が実際にどういう形を取るかはdata types sectionを参考に。

Comments

CQL中においてコメントは--か//で行を始める。複数行にわたるコメントは/*と*/で囲うこと。

-- This is a comment
// This is a comment too
/* This is
   a multi-line comment */

Statements

CQLはステートメントから成る。SQLと同様にこのステートメントは三つのカテゴリに分けられる:

すべての文はセミコロン(;)で終わる。ただし単一のステートメントを扱う場合は省くことができる。サポートしているステートメントは以下のセクションで示す。上記のステートメントの文法を記すのに、以下のような非終端の記号を用いる:

<identifier> ::= any quoted or unquoted identifier, excluding reserved keywords
 <tablename> ::= (<identifier> '.')? <identifier>

    <string> ::= a string constant
   <integer> ::= an integer constant
     <float> ::= a float constant
    <number> ::= <integer> | <float>
      <uuid> ::= a uuid constant
   <boolean> ::= a boolean constant
       <hex> ::= a blob constant

  <constant> ::= <string>
               | <number>
               | <uuid>
               | <boolean>
               | <hex>
  <variable> ::= '?'
               | ':' <identifier>
      <term> ::= <constant>
               | <collection-literal>
               | <variable>
               | <function> '(' (<term> (',' <term>)*)? ')'

  <collection-literal> ::= <map-literal>
                         | <set-literal>
                         | <list-literal>
         <map-literal> ::= '{' ( <term> ':' <term> ( ',' <term> ':' <term> )* )? '}'
         <set-literal> ::= '{' ( <term> ( ',' <term> )* )? '}'
        <list-literal> ::= '[' ( <term> ( ',' <term> )* )? ']'

    <function> ::= <ident>

  <properties> ::= <property> (AND <property>)*
    <property> ::= <identifier> '=' ( <identifier> | <constant> | <map-literal> )


注意として、実際にすべてのケースで上記の文法が有効なわけではない。とくに、<variable>と入れ子にされた<collection-literal>は<collection-literal>のなかで有効ではない。

<variable>は匿名にもなれば名前を持つこともできる。どちらもプリペアードステートメントで変数と結び付けられる。匿名と命名済みの変数の違いは参照が楽になるかどうかだけである。

<properties>はキースペースやテーブルを作ったり変更するのに使う。それぞれの<property>は単一の値かマッピングされた値を取る。詳しくはのちに触れる。

<tablename>はテーブルの特定に使う。キースペースが指定されていない状態では、<tablename>はアクティブなキースペースの該当テーブルを指す(キースペースの指定はUSEを使う)。

サポートされている<function>についてはfunctionsセクションを参照すること。

Prepared Statement

CQLはプリペアードステートメントをサポートしている。プリペアードステートメントはクエリを一度展開し、複数回違った値でそのクエリを実行するときに使う。

ステートメントにおいて列の値が求められているなら、<variable>を使うことができる。その場合は変数を結びつけられたステートメントが必要となる。それが用意できれば、それに実際の値を与えることでそれを実行できる。これを使う実際の手続きはCQLドライバに依存しているもので、このドキュメントでこれ以降には載せていない。

Data Definition

CREATE KEYSPACE

Syntax:

<create-keyspace-stmt> ::= CREATE KEYSPACE (IF NOT EXISTS)? <identifier> WITH <properties>


Sample:

CREATE KEYSPACE Excelsior
           WITH replication = {'class': 'SimpleStrategy', 'replication_factor' : 3};

CREATE KEYSPACE Excalibur
           WITH replication = {'class': 'NetworkTopologyStrategy', 'DC1' : 1, 'DC2' : 3}
            AND durable_writes = false;


CREATE KEYSPACEステートメントは新しくトップレベルのキースペースを作る。キースペースはレプリケーションの方式とテーブルセットのオプションを規定する一つのネームスペースである。キースペースの名前として使えるのは32文字以下の英数字である。識別子としてのキースペース名は大文字小文字の区別をしないので、そうしたければ引用識別子を使うこと。

CREATE KEYSPACEステートメントが<properties>としてサポートするのは以下である:

name kind mandatory default description
replication map yes The replication strategy and options to use for the keyspace.
durable_writes simple no true Whether to use the commit log for updates on this keyspace (disable this option at your own risk!).

replication <peroperty>は必須である。少なくともclassサブオプションまで指定しなければならない。ほかのサブオプションはclassに従うようになっている。デフォルトではCassandraは以下のclassをサポートしている:

既存のキースペース名で新たにキースペースを作ろうとすると、エラーが返される。IF NOT EXISTSを使ってそれをやると、ステートメントは結局なにもしないことになる。

USE

Syntax:

<use-stmt> ::= USE <identifier>

Sample:

USE myApp;

USEステートメントは既存のキースペース名と使うことで、現在の使用キースペースを変更する。

ALTER KEYSPACE

Syntax:

<create-keyspace-stmt> ::= ALTER KEYSPACE <identifier> WITH <properties>


Sample:

ALTER KEYSPACE Excelsior
          WITH replication = {'class': 'SimpleStrategy', 'replication_factor' : 4};


ALTER KEYSPACEステートメントは既存のキースペースのプロパティを変更する。サポートされる<properties>はCREATE TABLEと同じである。

DROP KEYSPACE

Syntax:

<drop-keyspace-stmt> ::= DROP KEYSPACE ( IF EXISTS )? <identifier>

Sample:

DROP KEYSPACE myApp;

DROP KEYSPACEステートメントはキースペースとそこに含まれるデータをただちに削除する。

キースペースが存在していなければステートメントはエラーを返す。

CREATE TABLE

Syntax:

<create-table-stmt> ::= CREATE ( TABLE | COLUMNFAMILY ) ( IF NOT EXISTS )? <tablename>
                          '(' <definition> ( ',' <definition> )* ')'
                          ( WITH <option> ( AND <option>)* )?

<column-definition> ::= <identifier> <type> ( STATIC )? ( PRIMARY KEY )?
                      | PRIMARY KEY '(' <partition-key> ( ',' <identifier> )* ')'

<partition-key> ::= <identifier>
                  | '(' <identifier> (',' <identifier> )* ')'

<option> ::= <property>
           | COMPACT STORAGE
           | CLUSTERING ORDER


Sample:

CREATE TABLE monkeySpecies (
    species text PRIMARY KEY,
    common_name text,
    population varint,
    average_size int
) WITH comment='Important biological records'
   AND read_repair_chance = 1.0;

CREATE TABLE timeline (
    userid uuid,
    posted_month int,
    posted_time uuid,
    body text,
    posted_by text,
    PRIMARY KEY (userid, posted_month, posted_time)
) WITH compaction = { 'class' : 'LeveledCompactionStrategy' };


CREATE TABLEステートメントは新しくテーブルを作る。それぞれのテーブルは行の集合である。CREATE COLUMNTABLEシンタックスはCREATE TABLEのエイリアスとしてサポートされている(これまでの開発の経緯からの理由で)。

IF NOT EXISTSを使わずに既存のテーブルと同じ名前でテーブルを作ろうとするとエラーが返される。使った場合で、すでに同名のテーブルが存在するなら、そのステートメントはなにもしない。

<tablename>

テーブルの名前として使えるのはキースペースと同じ条件である(32文字以内で英数字)。もしテーブルの名前だけで宣言されたなら、それは現在のキースペース内で作られる。既存のキースペースと共に宣言されればそのキースペースに作られる。

<column-definition>

CREATE TABLEステートメントはテーブルに入る行の列を定義する。列はその名前と型で宣言する。

テーブル内において、行は固有のPRIMARY KEYで識別される。そして行の定義時にはPRIMARY KEYも定義することが必須である。PRIMARY KEYとしてテーブル内の複数の列を指定することもできる。もしPRIMARY KEYが一つの列ならば、列定義の際に後ろにPRIMARY KEYを付ければいい。複数の列を使うなら、コンマ区切りのリストで定義する必要がある。

CREATE TABLE t (
    k int PRIMARY KEY,
    other text
)

is equivalent to

CREATE TABLE t (
    k int,
    other text,
    PRIMARY KEY (k)
)

Partition key and clustering columns

CQLでは列がPRIMARY KEYの並び順を決める。最初の列のキーをパーティションキーと呼ぶ。パーティションキーにはすべての行で同じパーティションキーを(テーブルさえまたがって)同じ物理ノードでは共有するという特徴がある。与えられたテーブルでの同一のパーティションキーを共有する行への挿入、更新、消去は分離されアトミックに行われる。パーティションキーは丸括弧を使うことで、複数の列から作ることもできる。

STATIC columns

いくつかの列はテーブル定義時STATICとして宣言できる。静的な列は同じパーティションの複数の行の間で共有される:

CREATE TABLE test (
    pk int,
    t int,
    v text,
    s text static,
    PRIMARY KEY (pk, t)
);
INSERT INTO test(pk, t, v, s) VALUES (0, 0, 'val0', 'static0');
INSERT INTO test(pk, t, v, s) VALUES (0, 1, 'val1', 'static1');
SELECT * FROM test WHERE pk=0 AND t=0;

最後のクエリはsの値としてstatic1を返す。なぜならsは静的で共有されている値であり、ここに書き込まれた最新の値はstatic1だからである。もしこの例での二つの行が異なるパーティションにあるなら(たとえばpkの値が異なるとき)、二度目のsへの書き込みによって最初の行のデータが代わるようなことはない。

静的な列に関していくつか制約がある:

<option>

CREATE TABLEステートメントは新しいテーブルを作成する際のいくつものオプションをサポートしている。これはWITHキーワードとともに使う。

オプションの中で最初にあるのはCOMPACT STORAGEである。CQL3以前のものとの互換を目的としたものである(詳細はwww.datastax.com/dev/blog/thrift-to-cql3)。これはディスクに保存されるデータのレイアウトをコンパクトにするものだが、テーブルの柔軟性や拡張性を落とす。COMPACT STORAGEのテーブルはコレクションや静的な列を持てず、少なくともクラスタリングされた列を一つだけ(0でも2以上でもなく)PRIMARY KEY以外でサポートしている。以上の理由から古いものを後方互換で動かす以外では使用が推奨されていない。

CLUSTERING OF|RDERというオプションがある。これはディスク上に保存される行データのオーダーに関わってくる。クラスタリングされた列の名前リストとともに使い、それらのオーダーをどうするかを決める(ascかdescか)。このオプションはSELECTのORDER BYが返す結果に影響を与える。

テーブル作成において<property>でサポートされるのは以下がある:

option kind default description
comment simple none A free-form, human-readable comment.
read_repair_chance simple 0.1 The probability with which to query extra nodes (e.g. more nodes than required by the consistency level) for the purpose of read repairs.
dclocal_read_repair_chance simple 0 The probability with which to query extra nodes (e.g. more nodes than required by the consistency level) belonging to the same data center than the read coordinator for the purpose of read repairs.
gc_grace_seconds simple 864000 Time to wait before garbage collecting tombstones (deletion markers).
bloom_filter_fp_chance simple 0.00075 The target probability of false positive of the sstable bloom filters. Said bloom filters will be sized to provide the provided probability (thus lowering this value impact the size of bloom filters in-memory and on-disk)
compaction map see below The compaction options to use, see below.
compression map see below Compression options, see below.
replicate_on_write simple true Whether to replicate data on write. This can only be set to false for tables with counters values. Disabling this is dangerous and can result in random lose of counters, don’t disable unless you are sure to know what you are doing
caching simple keys_only Whether to cache keys (“key cache”) and/or rows (“row cache”) for this table. Valid values are: all, keys_only, rows_only and none.

compaction options

compactionプロパティは少なくともclassサブオプションを定義しなければならない。classサブオプションはコンパクションストラテジクラスを使うかを決定する。デフォルトでサポートされるのはSizeTieredCompactionStrategyとLeveledCompactionStrategyである。カスタムストラテジはstring constantによるクラス名で用意される。残りのサブオプションは選んだクラスによって変わる。残りのサブオプションは以下である:

option supported compaction strategy default description
enabled all true A boolean denoting whether compaction should be enabled or not.
tombstone_threshold all 0.2 A ratio such that if a sstable has more than this ratio of gcable tombstones over all contained columns, the sstable will be compacted (with no other sstables) for the purpose of purging those tombstones.
tombstone_compaction_interval all 1 day The minimum time to wait after an sstable creation time before considering it for “tombstone compaction”, where “tombstone compaction” is the compaction triggered if the sstable has more gcable tombstones than tombstone_threshold.
min_sstable_size SizeTieredCompactionStrategy 50MB The size tiered strategy groups SSTables to compact in buckets. A bucket groups SSTables that differs from less than 50% in size. However, for small sizes, this would result in a bucketing that is too fine grained. min_sstable_size defines a size threshold (in bytes) below which all SSTables belong to one unique bucket
min_threshold SizeTieredCompactionStrategy 4 Minimum number of SSTables needed to start a minor compaction.
max_threshold SizeTieredCompactionStrategy 32 Maximum number of SSTables processed by one minor compaction.
bucket_low SizeTieredCompactionStrategy 0.5 Size tiered consider sstables to be within the same bucket if their size is within [average_size * bucket_low, average_size * bucket_high ] (i.e the default groups sstable whose sizes diverges by at most 50%)
bucket_high SizeTieredCompactionStrategy 1.5 Size tiered consider sstables to be within the same bucket if their size is within [average_size * bucket_low, average_size * bucket_high ] (i.e the default groups sstable whose sizes diverges by at most 50%).
sstable_size_in_mb LeveledCompactionStrategy 5MB The target size (in MB) for sstables in the leveled strategy. Note that while sstable sizes should stay less or equal to sstable_size_in_mb, it is possible to exceptionally have a larger sstable as during compaction, data for a given partition key are never split into 2 sstables

compressionプロパティのサブオプションとして下記が設定できる:

option default description
sstable_compression LZ4Compressor The compression algorithm to use. Default compressor are: LZ4Compressor, SnappyCompressor and DeflateCompressor. Use an empty string ('') to disable compression. Custom compressor can be provided by specifying the full class name as a string constant.
chunk_length_kb 64KB On disk SSTables are compressed by block (to allow random reads). This defines the size (in KB) of said block. Bigger values may improve the compression rate, but increases the minimum size of data to be read from disk for a read
crc_check_chance 1.0 When compression is enabled, each compressed block includes a checksum of that block for the purpose of detecting disk bitrot and avoiding the propagation of corruption to other replica. This option defines the probability with which those checksums are checked during read. By default they are always checked. Set to 0 to disable checksum checking and to 0.5 for instance to check them every other read

Other considerations:

ALTER TABLE

Syntax:

<alter-table-stmt> ::= ALTER (TABLE | COLUMNFAMILY) <tablename> <instruction>

<instruction> ::= ALTER <identifier> TYPE <type>
                | ADD   <identifier> <type>
                | DROP  <identifier>
                | WITH  <option> ( AND <option> )*


Sample:

ALTER TABLE addamsFamily
ALTER lastKnownLocation TYPE uuid;

ALTER TABLE addamsFamily
ADD gravesite varchar;

ALTER TABLE addamsFamily
WITH comment = 'A most excellent and useful column family'
 AND read_repair_chance = 0.2;


ALTER TABLEステートメントはテーブル定義を操作するために使う。新しい列を加える、既存の列を消す、列の型を変える、あるいはテーブルオプションを新しくするなど。テーブル作成と同様、ALTER COLUNFAMILYはALTER TABLEのエイリアスである。

<tablename>はキースペース名に先行するオプションである。<instruction>はどういう変更を施すかを定義する:

DROP TABLE

Syntax:

<drop-table-stmt> ::= DROP TABLE ( IF EXISTS )? <tablename>

Sample:

DROP TABLE worldSeriesAttendees;

DROP TABLEステートメントは即座にテーブルをテーブル内のデータとともに消去し、結果を巻き戻すことはできない。テーブル作成と同様にDROP COLUMNFAMILYはDROP TABLEのエイリアスである。

IF EXISTSを使わずに存在しないテーブルを消去しようとするとエラーを返す。

TRUNCATE

Syntax:

<truncate-stmt> ::= TRUNCATE <tablename>

Sample:

TRUNCATE superImportantData;

TRUNCATEステートメントはテーブルからすべてのデータを消す。

CREATE INDEX

Syntax:

<create-index-stmt> ::= CREATE ( CUSTOM )? INDEX ( IF NOT EXISTS )? <identifier>? ON <tablename> '(' <identifier> ')'
                                        ( USING <string> ( WITH OPTIONS = <map-literal> )? )?

Sample:

CREATE INDEX userIndex ON NerdMovies (user);
CREATE INDEX ON Mutants (abilityId);
CREATE CUSTOM INDEX ON users (email) USING 'path.to.the.IndexClass';
CREATE CUSTOM INDEX ON users (email) USING 'path.to.the.IndexClass' WITH OPTIONS = {'storage': '/mnt/ssd/indexes/'};

CREATE INDEXステートメントはセカンダリインデックスを作るのにつかわれる。インデックスを作るカラムの名前の前にONを使う。もしデータがすでにあるならば、非同期でインデックス化が行われる。インデックスが作られたあとで、新しいデータが列に入れられると、データ挿入と共に自動でインデックスに入れられる。

すでにインデックスが存在しているのにこのステートメントを使うとエラーを返す。このエラーはIF NOT EXISTSを使えば回避される。

DROP INDEX

Syntax:

<drop-index-stmt> ::= DROP INDEX ( IF EXISTS )? <identifier>

Sample:

DROP INDEX userIndex;

DROP INDEXステートメントはすでにあるセカンダリインデックスを消去するために使う。引数として消したいインデックスの名前を使う。IF EXISTSを使わずに存在しないインデックスを消去しようとするとエラーを返す。

Data Manipulation

INSERT

Syntax:

<insertStatement> ::= INSERT INTO <tablename>
                             '(' <identifier> ( ',' <identifier> )* ')'
                      VALUES '(' <term-or-literal> ( ',' <term-or-literal> )* ')'
                      ( IF NOT EXISTS )?
                      ( USING <option> ( AND <option> )* )?

<term-or-literal> ::= <term>
                    | <collection-literal>

<option> ::= TIMESTAMP <integer>
           | TTL <integer>


Sample:

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

INSERTステートメントは与えられた行への新しい列データの挿入を行う。行はPRIMARY KEYで特定されるもので、列は少なくともPRIMARY KEYを構成できるデータが必要になる。

SQLとの違いとして、INSERTは行の存在チェックを行わず、あるならアップデート、なければ新しく作成される。データの作成が行われたかアップデートが行われたかを突き止める価値はない。

挿入のみを行うためにIF NOT EXISTSを使うこともできる。しかしIF NOT EXISTSを使うと無視できないほどのパフォーマンスコストを支払うことになるので、使用は慎重に行いたい。

挿入によるすべてのアップデート処理はアトミックに独立して行われる。

UPDATEセクションを参照し、<option>で使用できるものの情報を得て、collectionsセクションで<collection-literal>の使い方も参照してほしい。またINSERTは、UPDATEのようなカウンターをサポートしていない。

UPDATE

Syntax:

<update-stmt> ::= UPDATE <tablename>
                  ( USING <option> ( AND <option> )* )?
                  SET <assignment> ( ',' <assignment> )*
                  WHERE <where-clause>
                  ( IF <condition> ( AND condition )* )?

<assignment> ::= <identifier> '=' <term>
               | <identifier> '=' <identifier> ('+' | '-') (<int-term> | <set-literal> | <list-literal>)
               | <identifier> '=' <identifier> '+' <map-literal>
               | <identifier> '[' <term> ']' '=' <term>

<condition> ::= <identifier> '=' <term>
              | <identifier> '[' <term> ']' '=' <term>

<where-clause> ::= <relation> ( AND <relation> )*

<relation> ::= <identifier> '=' <term>
             | <identifier> IN '(' ( <term> ( ',' <term> )* )? ')'
             | <identifier> IN '?'

<option> ::= TIMESTAMP <integer>
           | TTL <integer>


Sample:

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-clause>は更新する行を絞るために使い、PRIMARY KEYを含んだものでなければならない(INリレーションはパーティションキーの最後の列に対してのみサポートされている)。他の列の値はSETk-ワードの後の<assignment>によって指定される。

SQLとの違いとして、UPDATEは行の存在チェックを行わず、あるならアップデート、なければ新しく作成される。データの作成が行われたかアップデートが行われたかを突き止める価値はない。

更新のみを行うためにIFを使うこともできる。しかしIFを使うと無視できないほどのパフォーマンスコストを支払うことになるので、使用は慎重に行いたい。

挿入によるすべてのアップデート処理はアトミックに独立して行われる。

<assignment>におけるc=c+3という式はカウンタの更新を行う。'='のあとの前後の識別子は必須である(カウンタについてはインクリメント、デクリメントのみが可能で、値を適用することはできない)。

The id = id + <collection-literal> and id[value1] = value2 forms of <assignment> are for collections. Please refer to the relevant section for more details.

<options>

UPDATEとINSERTは挿入において以下に従う:

DELETE

Syntax:

<delete-stmt> ::= DELETE ( <selection> ( ',' <selection> )* )?
                  FROM <tablename>
                  ( USING TIMESTAMP <integer>)?
                  WHERE <where-clause>
                  ( IF ( EXISTS | ( <condition> ( AND <condition> )*) ) )?

<selection> ::= <identifier> ( '[' <term> ']' )?

<where-clause> ::= <relation> ( AND <relation> )*

<relation> ::= <identifier> '=' <term>
             | <identifier> IN '(' ( <term> ( ',' <term> )* )? ')'
             | <identifier> IN '?'

<condition> ::= <identifier> '=' <term>
              | <identifier> '[' <term> ']' '=' <term>


Sample:

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-clause>で消去するデータを選択できる(<selection>でのid[value]シンタクスはコレクションに使われる。詳しくはcollectionセクションを参照)。でなければ全ての行が消去される。<where-clause>は消去する行のキーを指定する(INリレーションはパーティションキーの最後の列に対してのみサポートされている)。

DELETEはUPDATEと同様にTIMESTAMPオプションをサポートする。statement.

BATCH

Syntax:

<batch-stmt> ::= BEGIN ( UNLOGGED | COUNTER ) BATCH
                 ( USING <option> ( AND <option> )* )?
                    <modification-stmt> ( ';' <modification-stmt> )*
                 APPLY BATCH

<modification-stmt> ::= <insert-stmt>
                      | <update-stmt>
                      | <delete-stmt>

<option> ::= TIMESTAMP <integer>


Sample:

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ステートメントは複数の操作(挿入、更新、消去)を一つのステートメントにまとめる。これの主な目的は二つある:

  1. クライアントとサーバ(あるいは調整サーバとレプリカ)のネットワーク間でのやり取りを抑える。
  2. BATCHで与えられたパーティションキーによるすべての更新は分離しアトミックに動作する
  3. デフォルトではバッチでのすべての処理はアトミックに行われる。

BATCHステートメントはUPDATE、INSERT、DELETEのみをサポートし、SQLトランザクションすべてをサポートするわけではない。

  • タイムスタンプの記述がなければ、すべてのオペレーションには同じタイムスタンプが付与される。
  • BATCHはTIMESTAMPオプションを、UPDATEステートメントと似たセマンティックでサポートする(タイムスタンプはバッチ内のステートメントによって適用される)。だがバッチ内のステートメントでTIMESTAMPを使ってはならない。

    Queries

    SELECT

    Syntax:

    <select-stmt> ::= SELECT <select-clause>
                      FROM <tablename>
                      ( WHERE <where-clause> )?
                      ( ORDER BY <order-by> )?
                      ( LIMIT <integer> )?
                      ( ALLOW FILTERING )?
    
    <select-clause> ::= DISTINCT? <selection-list>
                      | COUNT '(' ( '*' | '1' ) ')' (AS <identifier>)?
    
    <selection-list> ::= <selector> (AS <identifier>)? ( ',' <selector> (AS <identifier>)? )*
                       | '*'
    
    <selector> ::= <identifier>
                 | WRITETIME '(' <identifier> ')'
                 | TTL '(' <identifier> ')'
                 | <function> '(' (<selector> (',' <selector>)*)? ')'
    
    <where-clause> ::= <relation> ( AND <relation> )*
    
    <relation> ::= <identifier> <op> <term>
                 | '(' <identifier> (',' <identifier>)* ')' <op> <term-tuple>
                 | <identifier> IN '(' ( <term> ( ',' <term>)* )? ')'
                 | '(' <identifier> (',' <identifier>)* ')' IN '(' ( <term-tuple> ( ',' <term-tuple>)* )? ')'
                 | TOKEN '(' <identifier> ( ',' <identifer>)* ')' <op> <term>
    
    <op> ::= '=' | '<' | '>' | '<=' | '>='
    <order-by> ::= <ordering> ( ',' <odering> )*
    <ordering> ::= <identifer> ( ASC | DESC )?
    <term-tuple> ::= '(' <term> (',' <term>)* ')'
    


    Sample:

    SELECT name, occupation FROM users WHERE userid IN (199, 200, 207);
    
    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(*) FROM users;
    
    SELECT COUNT(*) AS user_count FROM users;
    
    


    SELECTステートメントはテーブルの中の任意の数の行から、任意の数の列を読み込む。行のセットを返し、それらはクエリに対応した列のまとまりを持っている。

    <select-clause>

    <select-clause>は必要な列を指定し、その結果のセットを返す。コンマ区切りで指定するか、ワイルドカード(*)を使う。

    <selector>は取り出す列の名前か、任意の数の列の名前の<function>である。ファンクションについては<term>と同様で、functionセクションで記述する。加えてこれらの一般的なファンクションはWRITETIMEファンクションは列が挿入されたときに作られたタイムスタンプに従う。

    いかなる<selector>もASキーワードを使うことでエイリアスとなる(サンプル参照)。<where-clause>と<order-by>で列を参照するときはオリジナルの名前で参照するべきで、エイリアスによって参照するべきではない。

    COUNTキーワードは*を丸括弧でおおって使う。使った場合、クエリはマッチした行の数のみを返す。COUNT(1)はエイリアスとしてサポートされている。

    <where-clause>

    <where-clause>にはクエリにかかる行を指定しなければならない。パーティションキーにおいて等価でない関係(INが単一の等価関係として使われた場合)はサポートされていない(だがパーティションキーにおいて非等価な関係のクエリを行うTOKENメソッドについて下で記している)。さらに、与えられたパーティションキーによってクラスタリングされた列は、行のオーダリングを行い、隣り合った行セットの選択のみが行われる。

    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の列をクエリするのにつかわれる。その場合、行はその値でなくPARTITIONKEYのトークンで選択される。キーのトークンは使用するパーティションに依存しており、ランダムパーティションは意味のある順序付けを行わない。パーティションの順序付けは常にバイトごとのトークン値の順序付けを行う(たとえパーティションキーの型がintだとして、特にtoken(-1) > token(0)でも)。例として:

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

    さらにINリレーションはパーティションキーの最後の列として、プライマリキーのフルセットで使うときにのみ有効である。

    またCLUSTERING COLUMNをグループ化して使うこともできる:

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

    これはblog_titleとして"John's Blog"という値をもち、posted_atに'2012-01-01'が入った行を返す。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'
    

    CLUSTERING COLUMNSで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'))
    

    <order-by>

    ORDER BYオプションはクエリ結果を順序付けするのに使う。列の名前を引数として取る。可能なオーダーは限られている(これはテーブルCLUSTERIG ORDERに依存している):

    LIMIT

    SELECTステートメントのLIMITオプションは結果として返す行の数を制限できる。

    ALLOW FILTERING

    デフォルトではCQLのSELECTクエリはフィルタリングを行わない。クエリは(生の)記録から(おそらく一部の)結果のセットを返す。フィルタをかけないクエリは決まった量の結果を返すため、実行時間が一定となる。

    ALLOW FILTERINGオプションはクエリ結果のフィルタリングを行う。ALLOW FILTERINGは一握りのレコードを絞り出すためにクラスタにあるすべてのデータに検索をかけ、予期できないパフォーマンスコストを支払うことになる。

    下記のテーブルはユーザーのプロフィール、生まれ年、国を保持している:

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

    下記のクエリは通る:

    SELECT * FROM users;
    SELECT firstname, lastname FROM users WHERE birth_year = 1981;
    

    なぜならどちらも、これらで返されるクエリに費やすパフォーマンスコストは一定だからである。もし1981年に生れたユーザがいないなら、二つ目のクエリはデータベースに保存されたユーザプロフィールの量に依存しなくなる(少なくとも直接は関係なくなる:なぜならセカンダリインデックスの実行を考えると、このクエリはクラスタ内でのノード数に依存し、保存されたデータ量には直接は依存しない。それでもノード数は常に保存されるデータ数より小さくなる)。もちろんどちらのクエリも多量の結果セットを返すかもしれないが、返されるデータの量は常にLIMITを使うことで制限をかけられる。

    下記のクエリは通らない:

    SELECT firstname, lastname FROM users WHERE birth_year = 1981 AND country = 'FR';
    

    なぜならCassandraは多量のデータにスキャンをかけることはしないからである、たとえクエリに対する結果が少ないものであったとしても。たとえその中でフランス生まれが一握りであったとしても、1981年生まれのユーザすべてをスキャンせねばならない。もしあなたが、自分自身でなにをしようとしているかわかっているなら、ALLOW FILTERINGを末尾に付けることでクエリを実行できる:

    SELECT firstname, lastname FROM users WHERE birth_year = 1981 AND country = 'FR' ALLOW FILTERING;
    

    Data Types

    CQLは列の値として、コレクションを含む豊富な型をサポートしている。それらに加えて、ユーザはカスタムされた型も使用できる(JAVAのAbstractTypeを拡張したクラスで行われる)。シンタクスは以下のとおり:

    <type> ::= <native-type>
             | <collection-type>
             | <string>       // Used for custom types. The fully-qualified name of a JAVA class
    
    <native-type> ::= ascii
                    | bigint
                    | blob
                    | boolean
                    | counter
                    | decimal
                    | double
                    | float
                    | inet
                    | int
                    | text
                    | timestamp
                    | timeuuid
                    | uuid
                    | varchar
                    | varint
    
    <collection-type> ::= list '<' <native-type> '>'
                        | set  '<' <native-type> '>'
                        | map  '<' <native-type> ',' <native-type> '>'
    

    ネイティブな型は大文字小文字を判別する。それらは予約語ではない。

    下の表はデータ型と型のサポート値に関してである:

    type constants supported description
    ascii strings ASCII character string
    bigint integers 64-bit signed long
    blob blobs Arbitrary bytes (no validation)
    boolean booleans true or false
    counter integers Counter column (64-bit signed value). See Counters for details
    decimal integers, floats Variable-precision decimal
    double integers 64-bit IEEE-754 floating point
    float integers, floats 32-bit IEEE-754 floating point
    inet strings An IP address. It can be either 4 bytes long (IPv4) or 16 bytes long (IPv6). There is no inet constant, IP address should be inputed as strings
    int integers 32-bit signed int
    text strings UTF8 encoded string
    timestamp integers, strings A timestamp. Strings constant are allow to input timestamps as dates, see Working with dates below for more information.
    timeuuid uuids Type 1 UUID. This is generally used as a “conflict-free” timestamp. Also see the functions on Timeuuid
    uuid uuids Type 1 or type 4 UUID
    varchar strings UTF8 encoded string
    varint integers Arbitrary-precision integer

    コレクションに関してもっと詳しい使い方は下のWorking with collectionsに示す。

    Working with dates

    timestamp型の値は64ビットのサイン付き整数で、ミリセカンドを表す。これはエポックにもとづいている:January 1 1970 at 00:00:00 GMT.

    タイムスタンプはCQLでシンプルなlong整数で入力でき、先に示したエポックを基準としたミリセカンドで与えられる。

    TまたISO8601のフォーマットで文字列で入力もでき、2011年3月2日午前4時5分GMTは下記の方法で記せる:

    +0000はRFC822 4デジットタイムゾーン記法であり。+0000はGMTを示す。アメリカの太平洋側の標準時は-0800である。タイムゾーンは削除もでき、Cassandraノードは日付が基準点での値だと解釈する。

    タイムゾーンの推定は難しいので、タイムスタンプには常にタイムゾーンが表記されることが推奨される。

    もし日にちしか必要なければ下記のようにも書ける:

    この場合、時間は表記されたかデフォルトのタイムゾーンで00:00:00と解釈される。

    Counters

    counter型はcounter columnを使うために定義する。カウンタ型となった列の値は64ビットの符号付整数で、二つの操作が可能である:インクリメントかデクリメントの(シンタクスはUPDATE参照)。カウンタの値をセットすることはできない。カウンタは最初のインクリメントかデクリメントをするまで存在せず、最初のそれを行うことによって前の値が0だったかのようにふるまう。カウンタの列を削除することはできるが、いくらかの制限がある(Cassandra Wikiを参照)。

    カウンタ型を使うには以下の制限がある:

    Working with collections

    Noteworthy characteristics

    コレクションは非正規化された小さな関係性のあるデータを、保存するためにある。たとえば"ユーザの電話番号"や"メールにつけるラベル"のようなものを扱うのによい。ただ際限なくデータが増えるアイテム("すべてのユーザへのメッセージ"や"センサによって引き起こされたイベント")は適していないので、ふさわしいテーブルを使って保存するべきである。コレクションには下記の制限がある:

    ここに書かれた制限は今後、緩和されることがあるかもしれない。

    Maps

    mapはキーとバリューのペアがセットになったものである。マップは常にキーによってソートされ、その結果が返される。map型の列を作る場合、コンマ区切りでキーとバリューのペアを、角括弧<>であたえる。

    CREATE TABLE users (
        id text PRIMARY KEY,
        given text,
        surname text,
        favs map<text, text>   // A map of text keys, and text values
    )
    

    mapの書き方はJSONのシンタクスと同様である。レコードをINSERTを使って書くとき、JSONのやり方で書けばいい。このやり方ではmapデータを丸々書き換えることに留意が必要。

    // Inserting (or Updating)
    INSERT INTO users (id, given, surname, favs)
               VALUES ('jsmith', 'John', 'Smith', { 'fruit' : 'apple', 'band' : 'Beatles' })
    

    mapへのキーバリューの追加、更新はUPDATEステートメントで行う。

    // Updating (or inserting)
    UPDATE users SET favs['author'] = 'Ed Poe' WHERE id = 'jsmith'
    UPDATE users SET favs = favs +  { 'movie' : 'Cassablanca' } WHERE id = 'jsmith'
    

    TTLはINSERTにもUPDATEにも適用されるが、TTLは新しい挿入、更新によってセットされる値だけに有効である。例文で言い換える。

    // Updating (or inserting)
    UPDATE users USING TTL 10 SET favs['color'] = 'green' WHERE id = 'jsmith'
    

    これはTTLを{ 'color' : 'green' }レコードのみに適用する。ほかのmap要素には影響しない。

    mapレコードの削除は以下のように行う:

    DELETE favs['author'] FROM users WHERE id = 'jsmith'
    

    Sets

    set型はユニークな値のコレクションである。セットはその値によってソートされる。set型の列を作るには、入れるデータの型を角括弧<>で囲う。

    CREATE TABLE images (
        name text PRIMARY KEY,
        owner text,
        date timestamp,
        tags set<text>
    );
    

    setデータを書くにはコンマ区切りで値を置いていき、丸括弧で囲う。INSERTを使うと丸々セットを置き換える。

    INSERT INTO images (name, owner, date, tags)
                VALUES ('cat.jpg', 'jsmith', 'now', { 'kitten', 'cat', 'pet' });
    

    値の追加および削除はUPDATEで、setでカラムを指定して行う。

    UPDATE images SET tags = tags + { 'cute', 'cuddly' } WHERE name = 'cat.jpg';
    UPDATE images SET tags = tags - { 'lame' } WHERE name = 'cat.jpg';
    

    mapsと同様、TTLは新しく挿入、更新される値に対してのみ有効である。

    Lists

    list型はユニークでない値の集合であり、順番が保持される。list型の列の値を作るには、listキーワードと共に角括弧で入れるデータの型を囲む。

    CREATE TABLE plays (
        id text PRIMARY KEY,
        game text,
        players int,
        scores list<int>
    )
    

    以下で説明するように、リストにはいくらかの制限とパフォーマンスでの考慮点があり、可能ならばリストよりセットを使うべきである。

    listデータはJSONスタイルのシンタクスで書ける。INSERTを使って書くならJSONでの配列と同じ書き方をする。INSERTを使うとリストを丸々置き換える。

    INSERT INTO plays (id, game, players, scores)
               VALUES ('123-afde', 'quake', 3, [17, 4, 2]);
    

    値の追加(先頭か末尾への)は既存の列のlist要素への新しいJSONスタイルで記述する。

    UPDATE plays SET players = 5, scores = scores + [ 14, 21 ] WHERE id = '123-afde';
    UPDATE plays SET players = 5, scores = [ 12 ] + scores WHERE id = '123-afde';
    

    appendやprependはベキ等性の処理ではない。これはappendかprependでタイムアウトすると、安全にリトライは行われない(これでレコードのappendやprependを二回行うことになる)ということである。

    リストは以下の命令もできる:位置を指定してのデータセット、および消去、与えられた値に一致する要素すべての削除など。しかしこれに反して他のコレクションと異なり、この三つの命令は更新前に読み込みをし、パフォーマンスを落とす。この命令は以下のシンタクスで記す:

    UPDATE plays SET scores[1] = 7 WHERE id = '123-afde';                // sets the 2nd element of scores to 7 (raises an error is scores has less than 2 elements)
    DELETE scores[1] FROM plays WHERE id = '123-afde';                   // deletes the 2nd element of scores (raises an error is scores has less than 2 elements)
    UPDATE plays SET scores = scores - [ 12, 21 ] WHERE id = '123-afde'; // removes all occurrences of 12 and 21 from scores
    

    mapsと同様、TTLは新しく挿入、更新された値に対してのみ有効である。

    Functions

    CQL3はいくらかのファンクションをサポートしている(今後も増える)。ファンクションは値に対してのみ有効で、個々の値を集約するようなファンクションはサポートしていない。サポートされているファンクションを以下に記す:

    Token

    tokenはファンクションは与えられたパーティションキーのトークンを計算する。トークンファンクションの正確な署名はクラスタに使われているテーブルと、そのパーティションに依存している。

    tokenの引数の型はパーティションキーの列の値の型に依存する。返される型は使用しているパーティションに依存する:

    クラスタでデフォルトのMurmur3Partitionerを使い、以下のようにテーブルが定義されたとする。

    CREATE TABLE users (
        userid text PRIMARY KEY,
        username text,
        ...
    )
    

    このときtokenファンクションは一つのtext型の引数を取り(クラスタリングされた列はなく、パーティションキーはプライマリキーそのものである)、返される型はbigintである。

    Uuid

    uuid functionはパラメータなしにバージョン4のUUIDを作成する。

    Timeuuid functions

    now

    nowファンクションは引数を取らず、新しいユニークなtimeuuidを作る(ステートメントが使われるときに実行される)。このメソッドは便利だが、WHEREにおいてセンスのない使われ方をする。

    SELECT * FROM myTable WHERE t = now()
    

    上記のクエリはなにも返さないだろう。返される値はユニークであることが保証されているのだから。

    minTimeuuid and maxTimeuuid

    minTimeuuid(あるいはmaxTimeuuid)ファンクションはtimestampの値t(タイムスタンプか日付の文字列)を取り、そこから適切なもっとも小さい(あるいはもっとも大きい)偽のtimeuuidを作る。

    SELECT * FROM myTable WHERE t > maxTimeuuid('2013-01-01 00:05+0000') AND t < minTimeuuid('2013-02-02 10:00+0000')
    

    上のクエリでのtimeuuidの値tは‘2013-01-01 00:05+0000’より古く、‘2013-02-02 10:00+0000’より若くなる。t >= maxTimeuuid('2013-01-01 00:05+0000')は‘2013-01-01 00:05+0000’で作成されたtimeuuidを選択せず、本質的にはt > maxTimeuuid('2013-01-01 00:05+0000')と書くことと等価である。

    注意:minTimeuuidとmaxTimeuuidによって作成された値を偽のUUIDと読んだ。なぜならそれらはRFC 4122で決められた時間ベースのUUID作成プロセスを順守していないためである。実際、これら二つのメソッドはユニークな値を返さない。よってこれらはクエリのためだけに使うべきである(例はこれまでのサンプルで示した)。この結果を挿入するのは賢いアイディアではない。

    dateOf and unixTimestampOf

    dateOfファンクションとunixTimestampOfファンクションはtimeuuidを引数として取り、タイムスタンプを埋め込んだ出力を出す。だがdateOfファンクションはtimestamp型で値を返し、unixTimestampOfファンクションはbigint型で値を返す。

    Blob conversion functions

    いくらかのファンクションはネイティブな型をバイナリデータ(blob)に変換する。すべての<native-type>typeはCQL3(例外としてblob)によってサポートされ、typeAsBlobファンクションはtype型の引数を取り、blobとしてそれを返す。反対に、blobAsTypeファンクションは64ビットのblobを引数に取り、それをbigint値に変換する。よってbigintAsBlob(3)は0x0000000000000003であり、blobAsBigint(0x0000000000000003)は3である。


    翻訳メモ
    アトミック(原始性) - 一つのトランザクションで行われる手続きが個々に分断されず、ひとまとまりとして扱われること。アトミックに実行されるといえば、トランザクション完遂に必要な手続きがすべて行われ、失敗した場合は一つも行われないということになる。