DML を使用したパーティション分割テーブルデータの更新

このページでは、パーティション分割テーブルに対するデータ操作言語(DML)のサポートの概要について説明します。

DML の詳細については、次をご覧ください。

この例で使用されているテーブル

次の JSON スキーマ定義は、このページの例で使用されているテーブルを表します。

mytable: 取り込み時間パーティション分割テーブル

    [
      {"name": "field1", "type": "INTEGER"},
      {"name": "field2", "type": "STRING"}
    ]

mytable2: 標準の(パーティション分割されていない)テーブル

    [
      {"name": "id", "type": "INTEGER"},
      {"name": "ts", "type": "TIMESTAMP"}
    ]

mycolumntable: ts TIMESTAMP 列を使用してパーティショニングされたパーティション分割テーブル

    [
      {"name": "field1", "type": "INTEGER"},
      {"name": "field2", "type": "STRING"}
      {"name": "field3", "type": "BOOLEAN"}
      {"name": "ts", "type": "TIMESTAMP"}
    ]

例にある COLUMN_ID は、操作する列の名前で置き換えます。

データの挿入

パーティション分割テーブルに行を追加するには、DML の INSERT ステートメントを使用します。

取り込み時間パーティション分割テーブルへのデータの挿入

DML ステートメントを使用して取り込み時間パーティション分割テーブルに行を追加する場合は、行を追加するパーティションを指定できます。_PARTITIONTIME 疑似列を使用してパーティションを参照します。

たとえば、次の INSERT ステートメントでは、mytable の 2017 年 5 月 1 日のパーティション(“2017-05-01”)に行を追加します。

INSERT INTO
  project_id.dataset.mytable (_PARTITIONTIME,
    field1,
    field2)
SELECT
  TIMESTAMP("2017-05-01"),
  1,
  "one"

厳密な日付境界に対応するタイムスタンプのみを使用できます。たとえば、次の DML ステートメントではエラーが返されます。

INSERT INTO
  project_id.dataset.mytable (_PARTITIONTIME,
    field1,
    field2)
SELECT
  TIMESTAMP("2017-05-01 21:30:00"),
  1,
  "one"

パーティション分割テーブルへのデータの挿入

DML を使用してパーティション分割テーブルにデータを挿入することは、パーティショニングされていないテーブルにデータを挿入することと同じです。

たとえば、次の INSERT ステートメントでは、mytable2(パーティショニングされていないテーブル)からデータを選択して、パーティション分割テーブル mycolumntable に行を追加します。

INSERT INTO
  project_id.dataset.mycolumntable (ts,
    field1)
SELECT
  ts,
  id
FROM
  project_id.dataset.mytable2

データの削除

パーティション分割テーブルから行を削除するには、DML の DELETE ステートメントを使用します。

取り込み時間パーティション分割テーブルのデータの削除

次の DELETE ステートメントでは、mytable の 2017 年 6 月 1 日のパーティション("2017-06-01")から、field121 に等しいすべての行を削除します。_PARTITIONTIME 疑似列を使用してパーティションを参照します。

DELETE
  project_id.dataset.mytable
WHERE
  field1 = 21
  AND _PARTITIONTIME = "2017-06-01"

パーティション分割テーブルのデータの削除

DML を使用してパーティション分割テーブルのデータを削除することは、パーティショニングされていないテーブルからデータを削除することと同じです。

たとえば、次の DELETE ステートメントでは、mycolumntable の 2017 年 6 月 1 日のパーティション("2017-06-01")から、field121 に等しいすべての行を削除します。

DELETE
  project_id.dataset.mycolumntable
WHERE
  field1 = 21
  AND DATE(ts) = "2017-06-01"

DML DELETE を使用してパーティションを削除する

修飾子付きの DELETE ステートメントがパーティション内のすべての行をカバーする場合、BigQuery はパーティション全体を削除します。この削除は、バイトをスキャンすることもスロットを消費することもなく行われます。次の DELETE ステートメントの例では、_PARTITIONDATE 疑似列のフィルタのパーティション全体を対象としています。

DELETE mydataset.mytable
WHERE _PARTITIONDATE IN ('2076-10-07', '2076-03-06');

よくある不承認の理由

次の特性を持つクエリは、最適化のメリットが得られない場合があります。

最適化の対象となるかどうかは、パーティショニングのタイプ、基盤となるストレージ メタデータ、フィルタ述語によって異なります。ベスト プラクティスとして、ドライランを実行して、クエリの結果で処理されたバイトが 0 バイトであることを確認します。

マルチステートメント トランザクション

この最適化は、マルチステートメント トランザクション内で機能します。次のクエリの例では、パーティションをスキャンして DELETE ステートメントを探すことなく、単一トランザクションでパーティションを別のテーブルのデータに置き換えます。

DECLARE REPLACE_DAY DATE;
BEGIN TRANSACTION;

-- find the partition which we want to replace
SET REPLACE_DAY = (SELECT MAX(d) FROM mydataset.mytable_staging);

-- delete the entire partition from mytable
DELETE FROM mydataset.mytable WHERE part_col = REPLACE_DAY;

-- insert the new data into the same partition in mytable
INSERT INTO mydataset.mytable
SELECT * FROM mydataset.mytable_staging WHERE part_col = REPLACE_DAY;

COMMIT TRANSACTION;

データの更新

パーティション分割テーブルの行を更新するには、UPDATE ステートメントを使用します。

取り込み時間パーティション分割テーブルのデータの更新

次の UPDATE ステートメントでは、あるパーティションから別のパーティションに行を移動します。 field121 に等しい mytable の 2017 年 5 月 1 日のパーティション(“2017-05-01”)の行が、2017 年 6 月 1 日のパーティション(“2017-06-01”)に移動します。

UPDATE
  project_id.dataset.mytable
SET
  _PARTITIONTIME = "2017-06-01"
WHERE
  _PARTITIONTIME = "2017-05-01"
  AND field1 = 21

パーティション分割テーブルのデータの更新

DML を使用してパーティション分割テーブルのデータを更新することは、パーティショニングされていないテーブルのデータを更新することと同じです。たとえば、次の UPDATE ステートメントでは、あるパーティションから別のパーティションに行を移動します。field121 に等しい mytable の 2017 年 5 月 1 日のパーティション(“2017-05-01”)の行が、2017 年 6 月 1 日のパーティション(“2017-06-01”)に移動します。

UPDATE
  project_id.dataset.mycolumntable
SET
  ts = "2017-06-01"
WHERE
  DATE(ts) = "2017-05-01"
  AND field1 = 21

時間、月、年単位のパーティション分割テーブルの DML

DML ステートメントを使用して、時間単位、月単位、または年単位のパーティション分割テーブルを変更できます。関連する日付 / タイムスタンプ / 日時の時間、月、年の範囲を指定します。月単位のパーティション分割テーブル用の例を次に示します。

    bq query --nouse_legacy_sql 'DELETE FROM my_dataset.my_table WHERE
    TIMESTAMP_TRUNC(ts_column, MONTH) = "2020-01-01 00:00:00";'

または、DATETIME 列を使用したパーティション分割テーブル用の別の例は次のとおりです。

    bq query --nouse_legacy_sql 'DELETE FROM my_dataset.my_table WHERE
    dt_column BETWEEN DATETIME("2020-01-01")
    AND DATETIME("2020-05-01");'

MERGE ステートメントの使用

DML の MERGE ステートメントを使用すると、パーティション分割テーブルに対する INSERTUPDATEDELETE オペレーションを 1 つのステートメントに結合してアトミックに実行できます。

MERGE ステートメント使用時のパーティションのプルーニング

パーティション分割テーブルに MERGE ステートメントを実行するときに、サブクエリ フィルタ、search_condition フィルタ、または merge_condition フィルタのいずれかでパーティショニング列を使用すると、スキャンされるパーティションを制限できます。ソーステーブル、ターゲット テーブル、またはその両方をスキャンするときに、プルーニングが発生することがあります。

以下の各例では、_PARTITIONTIME 疑似列をフィルタとして使用して取り込み時間パーティション分割テーブルを照会しています。

サブクエリを使用してソースデータのフィルタリングする

次の MERGE ステートメントでは、USING 句のサブクエリにより、ソーステーブルの _PARTITIONTIME 疑似列をフィルタしています。

MERGE dataset.target T
USING (SELECT * FROM dataset.source WHERE _PARTITIONTIME = '2018-01-01') S
ON T.COLUMN_ID = S.COLUMN_ID
WHEN MATCHED THEN
  DELETE

クエリ実行プランでは、まずサブクエリが実行されます。ソーステーブルの '2018-01-01' パーティションの行のみがスキャンされます。クエリプランの関連ステージは次のとおりです。

READ $10:name, $11:_PARTITIONTIME
FROM temp.source
WHERE equal($11, 1514764800.000000000)

when_clausesearch_condition でフィルタを使用する

search_condition にフィルタが含まれている場合、クエリ オプティマイザはパーティションをプルーニングしようとします。たとえば、次の MERGE ステートメントでは、WHEN MATCHED 句と WHEN NOT MATCHED 句に _PARTITIONTIME 疑似列のフィルタが含まれています。

MERGE dataset.target T
USING dataset.source S
ON T.COLUMN_ID = S.COLUMN_ID
WHEN MATCHED AND T._PARTITIONTIME = '2018-01-01' THEN
  UPDATE SET COLUMN_ID = S.COLUMN_ID
WHEN MATCHED AND T._PARTITIONTIME = '2018-01-02' THEN
  UPDATE SET COLUMN_ID = S.COLUMN_ID + 10
WHEN NOT MATCHED BY SOURCE AND T._PARTITIONTIME = '2018-01-03' THEN
  DELETE

結合ステージでは、ターゲット テーブルの '2018-01-01''2018-01-02''2018-01-03' の各パーティション(すべての search_condition フィルタのユニオン)のみがスキャンされます。

クエリ実行プランから:

READ
$1:COLUMN_ID, $2:_PARTITIONTIME, $3:$file_temp_id, $4:$row_temp_id
FROM temp.target
WHERE or(equal($2, 1514764800.000000000), equal($2, 1514851200.000000000), equal($2, 1514937600.000000000))

ただし、次の例では、WHEN NOT MATCHED BY SOURCE 句にフィルタ式がありません。

MERGE dataset.target T
USING dataset.source S
ON T.COLUMN_ID = S.COLUMN_ID
WHEN MATCHED AND T._PARTITIONTIME = '2018-01-01' THEN
  UPDATE SET COLUMN_ID = S.COLUMN_ID
WHEN NOT MATCHED BY SOURCE THEN
  UPDATE SET COLUMN_ID = COLUMN_ID + 1

このクエリは、WHEN NOT MATCHED BY SOURCE 句を計算するために、ターゲット テーブル全体をスキャンする必要があります。そのため、パーティションはプルーニングされません。

merge_condition で定数 false 述語を使用します。

WHEN NOT MATCHED 句と WHEN NOT MATCHED BY SOURCE 句を一緒に使用すると、BigQuery は通常、プルーニングできない完全な外部結合を実行します。ただし、マージ条件で false 述語が常に使用されている場合は、フィルタ条件を使用してパーティションをプルーニングできます。定数 false 述語の使用方法については、MERGE ステートメントmerge_condition 句の説明をご覧ください。

次の例では、ターゲット テーブルとソーステーブルの両方で '2018-01-01' パーティションのみをスキャンします。

MERGE dataset.target T
USING dataset.source S
ON FALSE
WHEN NOT MATCHED AND _PARTITIONTIME = '2018-01-01' THEN
  INSERT(COLUMN_ID) VALUES(COLUMN_ID)
WHEN NOT MATCHED BY SOURCE AND _PARTITIONTIME = '2018-01-01' THEN
  DELETE

merge_condition でフィルタを使用する

クエリ オプティマイザは、merge_condition に指定されたフィルタを使用してパーティションをプルーニングしようとします。結合の種類によって、クエリ オプティマイザが述語をテーブル スキャン ステージまでプッシュダウンできる場合と、できない場合があります。

次の例では、merge_condition が、ソーステーブルとターゲット テーブルを結合する述語として使用されています。クエリ オプティマイザは、両方のテーブルをスキャンするときにこの述語をプッシュダウンできます。その結果、クエリはターゲット テーブルとソーステーブルの両方で '2018-01-01' パーティションのみをスキャンします。

MERGE dataset.target T
USING dataset.source S
ON T.COLUMN_ID = S.COLUMN_ID AND
  T._PARTITIONTIME = '2018-01-01' AND
  S._PARTITIONTIME = '2018-01-01'
WHEN MATCHED THEN
  UPDATE SET COLUMN_ID = NEW_VALUE

次の例では、merge_condition にソーステーブルの述語が含まれていないため、ソーステーブルでパーティションのプルーニングを実行できません。ステートメントにはターゲット テーブルの述語が含まれていますが、ステートメントでは WHEN MATCHED 句ではなく、WHEN NOT MATCHED BY SOURCE 句を使用しています。つまり、一致しない行を見つけるには、クエリでターゲット テーブル全体をスキャンする必要があります。

MERGE dataset.target T
USING dataset.source S
ON T.COLUMN_ID = S.COLUMN_ID AND T._PARTITIONTIME = '2018-01-01'
WHEN NOT MATCHED BY SOURCE THEN
  UPDATE SET COLUMN_ID = NEW_VALUE

制限事項

DML の制限事項の詳細については、DML リファレンス ページの制限事項をご覧ください。

割り当て

DML の割り当て情報については、割り当てと上限ページの DML ステートメントをご覧ください。

料金

DML の料金については、パーティション分割テーブルの DML の料金をご覧ください。

テーブルのセキュリティ

BigQuery でテーブルへのアクセスを制御するには、テーブルのアクセス制御の概要をご覧ください。

次のステップ