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"

データの更新

パーティション分割テーブルの行を更新するには、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 でテーブルへのアクセスを制御するには、テーブルのアクセス制御の概要をご覧ください。

次のステップ