DynamoDBでSQLが使える!?DynamoDB+PartiQLで何が出来るのか試してみた

PartiQLでDynamoDBの操作が可能になったので色々試してみました。
2020.11.30

この記事は公開されてから1年以上経過しています。情報が古い可能性がありますので、ご注意ください。

はじめに

CX事業本部の佐藤智樹です。

先日DynamoDBについて以下の発表がありました。

え!?DynamoDBでSQL使えるようになるの?何がどこまでできるのか気になったのでドキュメントを参照しながら試してみたいと思います。

正確にはSQL互換のクエリ言語のPartiQLを使いますが、本稿ではSQLと呼称して記述していきます。

DynamoDB+PartiQLの操作についてはこちらのブログでも確認できます。こちらはAWS CLIを使用した操作について書かれています。

ドキュメント

ドキュメントは以下から確認できます。

アクセス方法は現状以下の3パターンあるようです。

  • DynamoDBのWebコンソール(新バージョンのみ)
  • AWS CLI
  • AWS SDK(Java)

今回はWebコンソールで操作を試してみます。

CRUD操作してみた

WebコンソールからSQLを実行します。まず最近でたDynamoDBの新画面を開きます。もし古い画面の方は以下から新画面を開いてください。

画面を開いたらSQLを実行する前に今回使用するテーブルをドキュメントに合わせて以下のように作成しておきます。

今回はCRUD操作の流れを一通り試すため、INSERT->SELECT->UPDATE->DELETEの流れで実施します。

操作はサイドバーの「PartiQL エディタ」から行います。

INSERT

以下のSQLでデータを追加してみます。

INSERT INTO Orders VALUE {'OrderID':'1', 'total':300}

実行すると以下の画面のようにデータ挿入が成功したことを確認できます。

Partition Keyがない場合やPrimary Keyが重複する場合はエラーになるとドキュメント上にあります(DynamoDBの特性上当然ではありますが…)念のため本当にエラーになるのか確認してみます。

Partition Keyがない場合:

INSERT INTO Orders VALUE {'total':300}

Primary Keyが重複する場合(最初のSQLを再実行):

INSERT INTO Orders VALUE {'OrderID':'1', 'total':300}

両方ちゃんとエラーになることが確認できました。

後1つだけ注意がいるのは複数件のデータを登録しようとした場合、Webコンソールだとクエリを同時に複数追加しようとするとエラーになります。

SELECT

次はデータを取得してみます。取得のために先にいくつかデータをSQLで追加しておきます。1件ずつ実行してください。

INSERT INTO Orders VALUE {'OrderID':'2','total':500};
INSERT INTO Orders VALUE {'OrderID':'3','total':900};
INSERT INTO Orders VALUE {'OrderID':'4','total':50};

まずは簡単に全件取得を実行します。

SELECT * FROM Orders

実行ボタンの下に実行結果が出力されます。

またJSONビューでデータの形式を確認することもできます。現状プレビューはできますが画面からデータのコピーはできないようです。

ドキュメントにも重要と書かれていますが、上記のように何も指定しない場合は裏側でFull Table Scanが走っているので状況によっては効率の悪い処理が実行されることになります。

Full Table Scan回避するために、WHERE句でPartition Keyを含めることが推奨されています。以下のようにPartition Keyを含めたクエリだとFull Table Scanを回避できます。

SELECT * 
FROM Orders 
WHERE OrderID = '1'
SELECT * 
FROM Orders 
WHERE OrderID = '1' AND total = 300
SELECT * 
FROM Orders 
WHERE OrderID IN ['1','2']

以下の場合は Full Table Scanが走るのでデータ量が多い場合、処理時間遅延が起きる可能性もあります。

/* Partition Keyの範囲検索 */
SELECT * 
FROM Orders 
WHERE OrderID > '1'
/* Partition Key以外の項目での検索 */
SELECT * 
FROM Orders 
WHERE total = 300
/* Partition Key以外の項目での検索が含まれる */
SELECT * 
FROM Orders 
WHERE OrderID = '1' OR total = 500

UPDATE

次は先ほどまでに登録していたデータを更新してみます。

UPDATE Orders 
  SET total=1000 
  SET item='update item'  
WHERE OrderID='1'

成功すると値は返却されませんが、再度Selectでデータを取得すると更新されていることが確認できます。 (UPDATE実行時にパラメータで指定すれば、変更前後の値を確認できます。詳しくはドキュメント参照)

さらにPartition Key以外でも更新できるのか確認してみます。

UPDATE Orders 
  SET item='update item 2'  
WHERE total=500

こちらはプライマリキーの属性を全て指定しなければ更新できないようです。 念のためパーティションキー、ソートキーのあるテーブルでパーティションキーだけ指定しましたが更新できませんでした。

また以下のようなパーティションキーの更新もできませんでした。これは従来のDynamoDBのAPIと同じ動きですね。

UPDATE Orders 
  SET OrderID='10'  
WHERE OrderID='4'

DELETE

CRUD操作の最後にレコードを削除してみます。 ドキュメントの注意事項にもある通り、一度に削除できるのは1アイテムまでのようです。

以下のコマンドで正常にデータが削除できました。またWHERE句の対象となるデータがない場合でも結果は正常で返ってきました。

DELETE FROM Orders
WHERE OrderID='4'

関数使ってみた

前の章のCRUD操作以外にもいくつかの関数が提供されているので紹介します。

Exists

SELECTした結果が存在すればtrue、存在しなければfalseを返す。 トランザクションでのみ使えるのでAWS CLIなどで試すことができます。コンソールでは以下のようなエラーとなります。

EXISTS(
  SELECT * 
  FROM Orders
  WHERE OrderID='1'
)

Begins_With

指定した属性の値で特定の文字列で始まる属性を抽出できます。

ただパーティションキーなどを指定しない場合は、おそらくですが内部的にはFull Table Scanが走っているのでデータ量が多くなる場合には注意が必要かもしれないです。

/* パーティションキー指定 */
SELECT * 
FROM Orders
WHERE begins_with(OrderID,'1')
/* キー項目以外を指定 */
SELECT * 
FROM Orders
WHERE begins_with(item,'update')

Missing

「IS Missing」で特定の属性を持っていないレコードを取得でき、「IS NOT Missing」で特定の属性を持つレコードが取得できます。

SELECT * 
FROM Orders
WHERE item IS Missing

SELECT * 
FROM Orders
WHERE item IS NOT Missing

Attribute_type

属性が特定の型であるか判定します。

SELECT * 
FROM Orders
WHERE Attribute_type(item, 'S')

Contains

属性の値で指定した文字列が部分一致する項目を返します。キー属性以外も指定できます。

SELECT * 
FROM Orders
WHERE Contains(item, 'update')

Size

指定した属性の文字数やバイト数を返します。n文字以下の値をもつレコードだけ抽出などができます。

SELECT * 
FROM Orders
WHERE size(item) < 12;

所感

以前までのDynamoDBの条件式などに比べると、初めてNoSQLを触る方にとっては格段にわかりやすい内容になってるかと思います。

ただ、NoSQLの特性を理解していないとSQLの裏側ではTable Full Scanが実行される頻度が上がりそうなので今後もDynamoDBの特性を意識してデータ抽出を行う部分は変わらなさそうです。

個人的に条件式は慣れている記法なのでかなり書きやすいです。いずれ性能をあまり意識しなくてもこれらのクエリが使えると良いなあと思ってます。

ざっくり色々試してみましたが誰かのためになれば幸いです。