SELECT

Cassandraテーブルからデータを取得します。

Cassandraテーブルからデータを取得します。

構文

SELECT select_expression FROM keyspace_name.table_name WHERE relation AND relation ...ORDER BY ( clustering_column(ASC | DESC)...) LIMIT n ALLOW FILTERING

select_expression(select式)は以下のとおりです。

selection_list | DISTINCT selection_list |(COUNT ( * | 1 ))
selection_listは以下のいずれかです。
  • パーティション・キーのリスト(DISTINCTとともに使用します)
  • selector AS alias, selector AS alias, ...| *

    aliasはカラム名の別名です。

selectorは以下のとおりです。

column name |(WRITETIME (column_name))|(TTL (column_name))|(function (selector , selector, ...))

functionは、timeuuid関数token関数、またはBLOB変換関数です。

relationは以下のとおりです。

column_name op term | ( column_name, column_name, ...) op term-tuple | column_name IN(term, ( term ...))| ( column_name, column_name, ...) IN(term-tuple, ( term-tuple ...))| TOKEN (column_name, ...) op(term)

opは、=、<、>、<=、>、=、CONTAINS、CONTAINS KEYのいずれかです。

term-tuple(Cassandra 2.1以降)は以下のとおりです。
( term, term, ... )
termは以下のとおりです。
  • 定数:文字列、数値、uuid、ブーリアン、16進数
  • バインド・マーカー(?)
  • 関数
  • セット:
    { literal, literal, ... }
  • リスト:
    [ literal, literal, ... ]
  • マップ:
    { literal :literal, literal :literal, ... }

構文の凡例

  • 大文字はリテラルを意味する
  • 小文字は、リテラルでないことを意味する
  • イタリック体は指定が任意であることを意味する
  • パイプ(|)記号はORまたはAND/ORを意味する
  • 省略記号(...)は繰り返し可能を意味する
  • 範囲記号「(」および「)」はリテラルではなく、範囲を示す

この構文には、CQL文の終了となるセミコロンは含まれていません。

説明

SELECT文は、Cassandraテーブルから1つ以上のレコードを読み取ります。SELECT文への入力はselect式です。select文の出力はselect式によって異なります。

表 1. Select式の出力
Select式 出力
カラムのリストのカラム キー値と一連のカラムを持つ行
COUNT集計関数 結果セット内の行数の値を1つのカラムに含む1行
DISTINCTパーティション・キー・リスト 他のカラム値とは異なるカラムの値
WRITETIME関数 カラムへの書き込みが発生した日時
TTL関数 カラムの残りのTime To Live

カラムの指定

SELECT式では、どのカラムが結果に表示されるかを指定します。アスタリスクを使用すると、すべてのカラムが選択されます。

SELECT * from People;

ビッグ・データ・アプリケーション内のカラムの値は重複します。パーティション・キーの固有の(異なる)値だけを返すには、DISTINCTキーワードを使用してください。

返された行のカウント

COUNT(*)を使用するSELECT式は、クエリーに一致した行数を返します。COUNT(1)を使用しても同じ結果を得ることができます。Cassandra 2.2.1以降では、COUNT(*)またはCOUNT(1)は、SELECT文内の他の集計関数またはカラムとともに使用できます。

usersテーブルの行数をカウントします。

SELECT COUNT(*) FROM users;

行数をカウントし、usersテーブル内の最大値を取得します。

SELECT max(name), name, COUNT(*) FROM users;
カラム名の別名を使用する機能は、select式でカラムに対してdateOf(created_at)などの関数呼び出しを使用する場合に特に便利です。
SELECT event_id, dateOf(created_at), blobAsText(content) FROM timeline;

カラム別名の使用

カラムの別名は、ASキーワードを使用して定義できます。

SELECT event_id, dateOf(created_at) AS creation_date, blobAsText(content) AS content FROM timeline;

出力では、カラムにわかりやすい名前が表示されます。

event_id | creation_date | content -------------------------+--------------------------+---------------- 550e8400-e29b-41d4-a716 | 2013-07-26 10:44:33+0200 | Some stuff

LIMITを使用した返される行の数の指定

LIMITオプションを使用して、クエリーによって返される行の数を制限するように指定できます。

SELECT COUNT(*) FROM big_table LIMIT 50000; SELECT COUNT(*) FROM big_table LIMIT 200000;

データベースに105,291行あった場合、上記の文の出力はそれぞれ50000行と105,291行になります。cqlshシェルには、デフォルトで10,000行という上限があります。Cassandraサーバーおよびネイティブ・プロトコルでは、誤った形式のクエリーを実行してシステムが不安定になるのを防ぐためにタイムアウトによってクエリーの実行が停止されますが、返される行の数は制限されていません。

FROMを使用したテーブルの指定

FROM句ではクエリー対象のテーブルを指定します。任意で、テーブルのキースペースを指定した後に、ピリオドとテーブル名を指定します。キースペースを指定しなかった場合は、現在のキースペースが使用されます。

たとえば、systemキースペースのIndexInfoテーブルの行数をカウントします。

SELECT COUNT(*) FROM system."IndexInfo";

WHEREを使用したデータのフィルター

WHERE句ではクエリー対象の行を指定します。WHERE句では、別名ではなく実際の名前を使用してカラムを参照してください。WHERE句内のカラムは次の要件のどちらかを満たす必要があります。WHERE句内のプライマリ・キーは、該当するデータが含まれている特定のノードをCassandraに伝えます。カラム名は、=またはIN演算子の左側に配置します。カラム値は演算子の右側に配置します。たとえば、empIDカラムとdeptIDカラムが次のテーブルのパーティション・キーの定義に含まれているため、WHERE句内のempIDを使用してすべてのカラムをクエリーできます。
CREATE TABLE emp ( empID int, deptID int, first_name varchar, last_name varchar, PRIMARY KEY (empID, deptID)); SELECT deptid FROM emp WHERE empid = 104;

Cassandraでは、WHERE句でのCONTAINS、CONTAINS KEY、IN、=、>、>=、<、または<=という条件演算子をサポートしていますが、特定の条件下ではすべてがサポートされるとは限りません。

条件の使用に関する制約

  • パーティション・キーの不等号条件演算子

    使用するパーティショナーにかかわらず、Cassandraでは、パーティション・キーにおいて不等号条件演算子をサポートしていません。パーティション・キーでの範囲のクエリーにはtoken関数を使用してください。

  • 複合パーティション・キーでのIN条件の併用

    IN条件は、パーティション・キーの最後のカラムで使用できます。ただし、キーのそれ以前のすべてのカラムの等価クエリーを行う必要があります。「INフィルター条件の使用」には、IN演算子の使用例が含まれています。

  • インデックス付きテーブルのクエリー

    インデックス付きテーブルのクエリーには、 「カラムのインデックス作成」に示すように、インデックス付きカラムに対して少なくとも1つの等価条件が必要です。

  • 範囲クエリー

    Cassandraは>または<比較をサポートしていますが、特定のパーティション・キーの場合、クラスター化カラムに対する条件は、Cassandraで連続した順序の行を選択できるフィルターに制限されます。

    例:
CREATE TABLE ruling_stewards ( steward_name text, king text, reign_start int, event text, PRIMARY KEY (steward_name, king, reign_start) );

次のクエリーは、reign(治世)が2450までに始まり2500より前に終わったstewards(執政)に関するデータを選択するフィルターを構築します。king(王)がプライマリ・キーのコンポーネントではなかった場合、以下のクエリーを使用するためにkingにインデックスを作成する必要があります。

SELECT * FROM ruling_stewards WHERE king = 'Brego' AND reign_start >= 2450 AND reign_start < 2500 ALLOW FILTERING;

出力は以下のようになります。

steward_name | king | reign_start | event --------------+-------+-------------+-------------------- Boromir | Brego | 2477 | Attacks continue Cirion | Brego | 2489 | Defeat of Balchoth (2 rows)

Cassandraで連続した順序の行を選択できるようにするには、等価条件を使用してフィルターにプライマリ・キーのkingコンポーネントを含める必要があります。ALLOW FILTERING句も必要です。ALLOW FILTERINGを使用すると、パフォーマンスが問題とならない場合、任意の条件を使用してクラスター化カラムをクエリーできます。

ALLOW FILTERING句

特定範囲の行を検索するなど潜在的に高負荷のクエリーを実行しようとすると、以下のプロンプトが表示されます。

Bad Request:Cannot execute this query as it might involve data filtering and thus may have unpredictable performance.If you want to execute this query despite the performance unpredictability, use ALLOW FILTERING.

このようなクエリーを実行するには、ALLOW FILTERING句を使用してください。メモリの使用量を減らすために、LIMIT n句を使用して制限することを推奨します。例:

Select * FROM ruling_stewards WHERE king = 'none' AND reign_start >= 1500 AND reign_start < 3000 LIMIT 10 ALLOW FILTERING;

ただし、LIMITを使用しても最悪の状況をまぬがれるわけではありません。たとえば、kingのないエントリーがなかった場合は、LIMITにかかわらずリスト全体をスキャンする必要があります。

データに関して収集する統計データを増やしていけば、おそらくALLOW FILTERINGはそれほど厳しいものではなくなります。たとえば、エントリーの90%にkingがないと知っていたら、そのようなエントリーを10個見つけるのは比較的負荷が低いことがわかります。

INフィルター条件の使用

カラムの複数の候補値を指定するには、WHERE句で等価条件演算子INを使用します。たとえば、従業員ID(プライマリ・キー)が105、107、または104である3つの行から、first_nameとlast_nameという2つのカラムを選択します。

SELECT first_name, last_name FROM emp WHERE empID IN (105, 107, 104);

IN条件テストの値はコンマで区切られたリストとして設定してください。リストは特定の範囲のカラム値で構成できます。

複合プライマリ・キーでのINによるフィルター

パーティション・キーの最後のカラムには、IN条件を使用することが推奨されます。ただし、キーのそれ以前のすべてのカラムの等価クエリーを行う必要があります。例:
CREATE TABLE parts (part_type text, part_name text, part_num int, part_year text, serial_num text, PRIMARY KEY ((part_type, part_name), part_num, part_year)); SELECT * FROM parts WHERE part_type='alloy' AND part_name='hubcap' AND part_num=1249 AND part_year IN ('2010', '2015');
INを使用する場合は、最後のカラム以外のクラスター化カラムに対する等価テストを省略できますが、このようなクエリーにはデータのフィルターが必要となる場合があり、パフォーマンスが予想できなくなることがあります。このようなクエリーにはALLOW FILTERINGを使用する必要があります。例:
SELECT * FROM parts WHERE part_num=123456 AND part_year IN ('2010', '2015') ALLOW FILTERING;

CQLでは、IN句内に値の空のリストを使用できるため、Javaドライバー・アプリケーションで空の配列を引数としてIN句に渡す場合に役立ちます。

INを使用すべきでない場合

インデックスを使用すべきでない場合の推奨事項は、WHERE句でINを使用するときにも当てはまります。ほとんどの条件下では、WHERE句にINを使用しないことを推奨します。INを使用すると、通常は多くのノードを対象にクエリーを実行する必要が生じるため、パフォーマンスが低下する可能性があります。たとえば、ノードが30、レプリケーション係数が3、および整合性レベルがLOCAL_QUORUMの1つのローカル・データ・センター・クラスターでは、キーを1つ指定したクエリーは2つのノードに送られますが、クエリーでIN条件を使用すると、キーがトークンの範囲のどこに入るかによってクエリー対象のノード数が増え、最大で20になる可能性があります。

クラスター化カラムの比較

Cassandra 2.0.6以降では、パーティション・キーとクラスター化カラムをグループ分けし、タプルと値を比較することで、パーティションをスキャンできます。例:
SELECT * FROM ruling_stewards WHERE (steward_name, king) = ('Boromir', 'Brego');

WHERE句で使用される構文は、タプルとしてのsteward_nameとkingのレコードを、Boromir、Bregoタプルと比較します。

順序付けられていない結果全体のページング

TOKEN関数は、クエリー対象のパーティション・キー・カラムに対して条件演算子とともに使用できます。その場合、クエリーは、パーティション・キーの値ではなく、そのトークンに基づいて行を選択します。キーのトークンは、使用しているパーティショナーによって異なります。RandomPartitionerまたはMurmur3Partitionerとともに使用すると、有意義な順序が得られません。

たとえば、以下のテーブルを定義したとします。

CREATE TABLE periods ( period_name text, event_name text, event_date timestamp, weak_race text, strong_race text, PRIMARY KEY (period_name, event_name, event_date) );

データの挿入後に、以下のクエリーではパーティション・キーを使用してデータを検索するためにTOKEN関数を使用します。

SELECT * FROM periods WHERE TOKEN(period_name) > TOKEN('Third Age') AND TOKEN(period_name) < TOKEN('Fourth Age');

複合プライマリ・キーの使用と結果のソート

ORDER BY句では1つのカラムだけを選択できます。そのカラムは、複合プライマリ・キーの2番目のカラムである必要があります。これは、プライマリ・キーに複数のカラム・コンポーネントがあるテーブルにも当てはまります。順序指定は昇順または降順で行うことができ、デフォルトは昇順です。ASCまたはDESCキーワードを使用して指定します。

ORDER BY句では、別名ではなく実際の名前を使用してカラムを参照してください。

たとえば、複合プライマリ・キーを使用するplaylistsテーブルを設定し、サンプル・データを挿入します。以下のクエリーを使用してsong_orderで順序が指定された特定のプレイリストに関する情報を取得します。select式にORDER BYカラムを含める必要はありません。

SELECT * FROM playlists WHERE id = 62c36092-82a1-3a00-93d1-46196ee77204 ORDER BY song_order DESC LIMIT 50;

出力は以下のようになります。

または、プレイリストのアーティストにインデックスを作成し、以下のクエリーを使用してそのプレイリストでFu Manchuの曲のタイトルを取得します。

CREATE INDEX ON playlists(artist) SELECT album, title FROM playlists WHERE artist = 'Fu Manchu';

出力は以下のようになります。

コレクション・セット、リスト、またはマップのフィルター

コレクション全体を取得するためにコレクションが含まれているテーブルを対象にクエリーを実行できます。コレクション・カラムを対象にインデックスを作成してから、WHERE句にCONTAINS条件を指定してコレクションに属する特定の値のデータをフィルターできます。音楽サービスの例を使用すると、playlistsテーブルにタグのコレクションを追加し、タグ・データを追加し、タグにインデックスを作成した後に、そのタグ・セットで'blues'をフィルターできます。

SELECT album, tags FROM playlists WHERE tags CONTAINS 'blues';

音楽会場(venue)マップを対象にインデックスを作成したら、'The Fillmore'などのマップ値でフィルターできます。
SELECT * FROM playlists WHERE venue CONTAINS 'The Fillmore';
venueマップのコレクション・キーを対象にインデックスを作成したら、マップ・キーでフィルターできます。
SELECT * FROM playlists WHERE venue CONTAINS KEY '2014-09-22 22:00:00-0700';

マップのエントリーのフィルター

コレクションを含むテーブルをクエリーし、マップのエントリーに基づいて行を取得できます。このオプションにはマップのみを使用できます。
CREATE INDEX blist_idx ON cycling.birthday_list (ENTRIES(blist));
次のクエリーは、テーブルbirthday_listblistマップ内のエントリーに基づいて、23歳のサイクリスト全員を検索します。
SELECT * FROM cycling.birthday_list WHERE blist['age'] = '23';

frozenのフル・コレクションのフィルター

FROZENコレクション(セット、リスト、またはマップ)を含むテーブルをクエリーし、コレクションの値に完全に一致する行を取得できます。
CREATE INDEX rnumbers_idx ON cycling.race_starts (FULL(rnumbers));
次のクエリーは、39回のプロ優勝、7回のグランド・ツアー出場、および14回のクラシック出場の記録を持つサイクリストをfrozenリストから検索します。
SELECT * FROM cycling.race_starts WHERE rnumbers = [39,7,14];

書き込みが発生した日時の取得

WRITETIMEの後にカラム名を丸かっこで囲んで指定すると、カラムがデータベースに書き込まれた日時がマイクロ秒単位で返されます。

名前がJonesというユーザーのfirst_nameカラムに書き込みが発生した日時を取得します。

SELECT WRITETIME (first_name) FROM users WHERE last_name = 'Jones'; writetime(first_name) ----------------------- 1353010594789000

マイクロ秒単位のwritetime出力を変換すると、「November 15, 2012 at 12:16:34 GMT-8」になります。