DQLを使ってDynamoDBをSQL操作する

AWS には NoSQL データベース DynamoDBが存在します。 標準では

  • ウェブ管理画面
  • AWS CLI
  • SDK を使ったプログラム

から操作することもでるものの、地味に面倒です。 今回は DQL を使ってシェルインターフェースから DynamoDB を SQL 操作する方法を紹介します。

DQL とは?

DQL は "DynamoDB Query Language" の略で DynamoDB 向けの SQL 風インターフェースです。

DynamoDB テーブルの作成・更新・削除からレコードの追加・参照・更新・削除を SQL 風に操作できます。

サクッと DynamoDB の中を覗く場合には、今回紹介するようなツールを使うのがお手軽ではないかと思います。

インストール

DQL は Python で書かれているため pip でインストールします。

$ pip install -U dql
$ dql --version
0.5.22

SQL 実行方法

SQL の実行方法は

  • シェル
  • コマンドライン

の2通りがあります。

シェルから SQL を実行

シェル起動時にリージョンを指定します。たとえば日本リージョンの場合は次のようになります。

$ dql -r ap-northeast-1
ap-northeast-1>

リージョンを指定しなかった場合は N. Virginia に接続します。

$ dql
us-west-1>

環境変数 AWS_REGION が設定されている場合は、そのリージョンが優先されます。

$ export AWS_REGION=ap-northeast-1
$ dql
ap-northeast-1>

以降の例ではシェルモードから SQL を実行します。

コマンドライン から SQL を実行

sql -c に続けて SQL を投げます。

$ dql -c 'SCAN * FROM Movies'

シェルと同じ要領でAWSリージョンを指定します。例えば、東京リージョンに対して SQL を投げる場合は次のようになります。

$ dql -r ap-northeast-1 -c 'SCAN * FROM Movies'

以下ではシェルモードで操作します。

テーブルの作成

CREATE 文でテーブルを作成します。

eu-central-1> CREATE TABLE Movies (
            >   year NUMBER HASH KEY,
            >   title STRING RANGE KEY,
            >   THROUGHPUT (10, 10));
Created table 'Movies'
eu-central-1> ls
Name   Status Read Write
Movies ACTIVE 10   10

DUMP 文でテーブルスキーマを確認できます。

リージョン内の全テーブル

eu-central-1> DUMP SCHEMA;
CREATE TABLE Movies (year NUMBER HASH KEY, title STRING RANGE KEY, THROUGHPUT (10, 10));

リージョン内の特定のテーブル

eu-central-1> DUMP SCHEMA Table1,Table2;
...;

アイテムの作成

INSERT 文でアイテムを作成します。

eu-central-1> insert into Movies(
            >   year=2015,
            >   title="The Big New Movie",
            >   info={
            >     "plot": "Nothing happens at all.",
            >     "rating" : 0});
Inserted 1 item

アイテムの参照

DynamoDB テーブルのスキャンには SCAN 文を使用します。。

eu-central-1> SCAN * FROM Movies;
-----------------------------------------------------------------------------------------------
|                              info                              |        title        | year |
-----------------------------------------------------------------------------------------------
| {u'plot': u'Nothing happens at all.', u'rating': Decimal('0')} | 'The Big New Movie' | 2015 |
-----------------------------------------------------------------------------------------------

DynamoDB テーブルのクエリーには SELECT 文を使用します。。

eu-central-1> SELECT * FROM Movies WHERE year=2015;
-----------------------------------------------------------------------------------------------
|                              info                              |        title        | year |
-----------------------------------------------------------------------------------------------
| {u'plot': u'Nothing happens at all.', u'rating': Decimal('0')} | 'The Big New Movie' | 2015 |
-----------------------------------------------------------------------------------------------

インデックスを指定せずにテーブル全体を SELECT しようとすると、以下のようなエラーが発生します。

eu-central-1> SELECT * FROM Movies;
...
SyntaxError: No index found for query. Please use a SCAN query, or set allow_select_scan=True
opt allow_select_scan true

一つ前の例のようにSCAN 文を使うか、次のように allow_select_scan オプションを変更します。

# 現在のオプションを確認
eu-central-1> opt allow_select_scan
allow_select_scan: False
# オプションを変更
eu-central-1> opt allow_select_scan true
# 現在のオプションを確認
eu-central-1> opt allow_select_scan
allow_select_scan: True
# テーブル全体を SELECT
eu-central-1> SELECT * FROM Movies;
-----------------------------------------------------------------------------------------------
|                              info                              |        title        | year |
-----------------------------------------------------------------------------------------------
| {u'plot': u'Nothing happens at all.', u'rating': Decimal('0')} | 'The Big New Movie' | 2015 |
-----------------------------------------------------------------------------------------------

アイテムの更新

UPDATE 文でアイテムを更新します。

UPDATE 式には AWS ドキュメントにある式を使えます。

実際に info 属性を変更してみましょう。

eu-central-1> UPDATE Movies
            > SET info.rating = 5.5, info.plot= "Everything happens all at once.", info.actors= ["Larry", "Moe", "Curly"]
            > WHERE year = 2015 AND title = "The Big New Movie";
Updated 1 item
eu-central-1> SCAN * FROM Movies;
--------------------------------------------------------------------------------------------------------------------------------------------------
|                                                        info                                                       |        title        | year |
--------------------------------------------------------------------------------------------------------------------------------------------------
| {u'plot': u'Everything happens all at once.', u'actors': [u'Larry', u'Moe', u'Curly'], u'rating': Decimal('5.5')} | 'The Big New Movie' | 2015 |
--------------------------------------------------------------------------------------------------------------------------------------------------

無事変わっていますね。

アイテムの削除

DELETE 文でアイテムを削除します。

eu-central-1> DELETE FROM Movies WHERE year=2015 AND title="The Big New Movie";
Deleted 1 item

WHERE を指定しなければ、テーブル全体を削除してしまうため、更新前に確認が求められます。

eu-central-1> DELETE FROM Movies;
This will run delete_item on all items in the table! Continue? [y/N] y
Deleted 1 item
eu-central-1> SCAN * FROM Movies;
No results

アイテムのファイル入出力

ファイル出力

SELECT 文に SAVE オプションを渡すと、ファイル出力できます。

eu-central-1> SELECT * FROM Movies SAVE Movies.json;
Saved 1 record to Movies.json

出力されたファイルの中身を確認してみましょう。

$ cat Movies.json
{"info":{"plot":"Everything happens all at once.","actors":["Larry","Moe","Curly"],"rating":5.5},"title":"The Big New Movie","year":2015}

デフォルトでは Python の Pickle 形式でシリアライズされます。

.csv, .json, .gz, .gzip などを出力ファイル名に含めると、それぞれの形で展開されます。

バイナリーデータなどが含まれる場合、 .csv, .json 出力すると、正しく出力されないため、お気をつけ下さい。

ファイル入力

LOAD 文でファイルからアイテムを投入できます。

eu-central-1> LOAD Movies.json INTO Movies;
Loaded 1 item

DynamoDB へのクエリー文の確認

EXPLAIN 文で SQL がどういうクエリーに展開されるのか確認できます。

eu-central-1> EXPLAIN SELECT * FROM Movies WHERE title = "The Big New Movie" and year = 2015;
query {'ConsistentRead': False,
 'ExpressionAttributeNames': {'#f1': 'year'},
 'ExpressionAttributeValues': {':v1': {'S': u'The Big New Movie'},
                               ':v2': {'N': u'2015'}},
 'KeyConditionExpression': u'(title = :v1 AND #f1 = :v2)',
 'ReturnConsumedCapacity': 'INDEXES',
 'ScanIndexForward': True,
 'TableName': 'Movies'}

テーブルの削除

DROP 文でテーブルを削除します。

# テーブル一覧を確認
eu-central-1> ls
Name   Status Read Write
Movies ACTIVE 10   10
# テーブルを削除
eu-central-1> DROP TABLE Movies;
Dropped table 'Movies'
# テーブル一覧を確認
eu-central-1> ls
Name Status Read Write

まとめ

今回は DQL を使ってシェルインターフェースから DynamoDB を SQL 操作する方法を紹介しました。 類似ツールに @winebarrel さん作成の ddbcli があります。

DQL と ddbcli が目指すところや機能に大きな差はありませんが、DQL はドキュメントがしっかりしている点が優位かなと思います。

DynamoDB を SQL 操作したい場合は ddbcli だけでなく DQL も一度検討されてはいかがでしょうか。

参考