修改資料表結構定義

本文說明如何修改現有 BigQuery 資料表的結構定義。

您可以使用 SQL 資料定義語言 (DDL) 陳述式,進行本文件所述的大多數結構定義修改。這些對帳單不會產生費用。

您可以將資料表資料匯出至 Cloud Storage,然後將資料載入至具有修改後結構定義定義的新資料表,藉此以本頁所述的所有方式修改資料表結構定義。BigQuery 載入和擷取工作不收費,但將匯出的資料儲存在 Cloud Storage 會產生費用。以下各節說明執行各種結構定義修改作業的其他方式。

在 BigQuery 更新結構定義不會導致資料遺失。

新增資料欄

您可以使用下列其中一種方法,在現有資料表的結構定義中新增資料欄:

  • 新增空白資料欄。
  • 使用載入或查詢工作覆寫資料表。
  • 透過載入或查詢工作,將資料附加至資料表。

您新增的任何資料欄都必須遵守 BigQuery 的資料欄名稱規則。如要進一步瞭解如何建立結構定義元件,請參閱「指定結構定義」。

您無法在資料表結構定義的中間新增資料欄。新的資料欄和巢狀欄位一律會新增至資料表或欄位的結尾。如要在資料表結構定義中間建立新資料欄,唯一的方法是使用所選結構定義建立新資料表,並從原始資料表複製資料。

新增空白資料欄

新增資料欄至現有的資料表結構定義時,資料欄必須為 NULLABLE 或 REPEATED。您無法將 REQUIRED 資料欄新增至現有的資料表結構定義。在 API 或 bq 指令列工具中,將 REQUIRED 資料欄新增至現有資料表結構定義會導致錯誤。不過,您可以建立巢狀 REQUIRED 欄,做為新 RECORD 欄位的一部分。只有在載入資料時建立資料表,或使用結構定義定義建立空白資料表時,才能新增 REQUIRED 資料欄。

如何將空白資料欄新增至資料表的結構定義:

控制台

  1. 前往 Google Cloud 控制台的「BigQuery」頁面。

    前往「BigQuery」

  2. 點選左側窗格中的 「Explorer」。

    醒目顯示的「Explorer」窗格按鈕。

    如果沒有看到左側窗格,請按一下 「Expand left pane」(展開左側窗格),開啟窗格。

  3. 在「Explorer」窗格中展開專案,點選「Datasets」(資料集),然後選取資料集。

  4. 依序點選「總覽」>「資料表」,然後選取資料表。

  5. 在詳細資料窗格中,依序點選「Edit」(編輯) >「Edit schema」(編輯結構定義)。

  6. 在「Current schema」(目前結構定義) 頁面的「New fields」(新欄位) 下方,點選「Add field」(新增欄位)。

    • 在「Name」(名稱) 部分,輸入資料欄名稱。
    • 針對「Type」(類型),請選擇資料類型。
    • 針對「Mode」(模式),請選擇 NULLABLE 或 REPEATED。
  7. 資料欄新增完畢後,按一下 [Save] (儲存)。

SQL

使用 ALTER TABLE ADD COLUMN DDL 陳述式:

  1. 前往 Google Cloud 控制台的「BigQuery」頁面。

    前往「BigQuery」

  2. 在查詢編輯器中輸入下列陳述式:

    ALTER TABLE mydataset.mytable
    ADD COLUMN new_column STRING;

  3. 按一下「 執行」。

如要進一步瞭解如何執行查詢,請參閱「執行互動式查詢」。

bq

發出 bq update 指令並提供 JSON 結構定義檔。如果要更新的資料表位於預設專案以外的專案,請按照下列格式,將專案 ID 新增至資料集名稱:PROJECT_ID:DATASET。

bq update PROJECT_ID:DATASET.TABLE SCHEMA

更改下列內容:

  • PROJECT_ID:您的專案 ID。
  • DATASET:資料集名稱,其中包含您要更新的資料表。
  • TABLE:要更新的資料表名稱。
  • SCHEMA:本機電腦上的 JSON 結構定義檔案路徑。

指定內嵌結構定義時,無法指定資料欄說明、模式和 RECORD (STRUCT) 類型。所有資料欄模式預設為 NULLABLE。因此如要新增巢狀資料欄至 RECORD,就必須提供 JSON 結構定義檔。

如果您嘗試使用內嵌結構定義新增資料欄,則必須提供包括新資料欄在內的整個結構定義。由於您無法使用內嵌結構定義定義指定資料欄模式,因此更新會將任何現有的 REPEATED 資料欄變更為 NULLABLE,進而產生下列錯誤:BigQuery error in update operation: Provided Schema does not match Table PROJECT_ID:dataset.table. Field field has changed mode from REPEATED to NULLABLE.

使用 bq 指令列工具在現有資料表中新增資料欄時,建議提供 JSON 結構定義檔案。

如何使用 JSON 結構定義檔案將空白資料欄新增至資料表的結構定義:

  1. 首先,請發出 bq show 指令並加上 --schema 旗標,並將現有的資料表結構定義寫入檔案。如果您要更新的資料表位於預設專案以外的專案,請按照下列格式將專案 ID 新增至資料集名稱:PROJECT_ID:DATASET。

    bq show \
    --schema \
    --format=prettyjson \
    PROJECT_ID:DATASET.TABLE > SCHEMA

    更改下列內容:

    • PROJECT_ID:您的專案 ID。
    • DATASET:資料集名稱,其中包含您要更新的資料表。
    • TABLE:要更新的資料表名稱。
    • SCHEMA:寫入本機電腦的結構定義定義檔。

    舉例來說,如要將 mydataset.mytable 的結構定義寫入檔案,請輸入下列指令。mydataset.mytable 位於您的預設專案中。

       bq show \
       --schema \
       --format=prettyjson \
       mydataset.mytable > /tmp/myschema.json
    
  2. 在文字編輯器中開啟結構定義檔。結構定義應如下所示:

    [
      {
        "mode": "REQUIRED",
        "name": "column1",
        "type": "STRING"
      },
      {
        "mode": "REQUIRED",
        "name": "column2",
        "type": "FLOAT"
      },
      {
        "mode": "REPEATED",
        "name": "column3",
        "type": "STRING"
      }
    ]
    
  3. 將資料欄新增至結構定義的結尾處。如果嘗試在陣列的其他位置新增資料欄,系統會傳回以下錯誤:BigQuery error in update operation: Precondition Failed。在資料表建立後修改結構定義順序,不會影響資料欄或巢狀欄位的順序。

    您可以使用 JSON 檔案為新資料欄指定說明、NULLABLE 或 REPEATED 模式以及 RECORD 類型。例如,如果使用上一個步驟中的結構定義,新的 JSON 陣列會如下所示。此範例中會新增名為 column4 的 NULLABLE 資料欄,且 column4 包含說明。

      [
        {
          "mode": "REQUIRED",
          "name": "column1",
          "type": "STRING"
        },
        {
          "mode": "REQUIRED",
          "name": "column2",
          "type": "FLOAT"
        },
        {
          "mode": "REPEATED",
          "name": "column3",
          "type": "STRING"
        },
        {
          "description": "my new column",
          "mode": "NULLABLE",
          "name": "column4",
          "type": "STRING"
        }
      ]
      

    如要進一步瞭解如何使用 JSON 結構定義檔,請參閱指定 JSON 結構定義檔一節。

  4. 更新結構定義檔後,請發出下列指令來更新資料表的結構定義。如果要更新的資料表位於預設專案以外的專案,請按照下列格式,將專案 ID 新增至資料集名稱:PROJECT_ID:DATASET。

    bq update PROJECT_ID:DATASET.TABLE SCHEMA

    更改下列內容:

    • PROJECT_ID:您的專案 ID。
    • DATASET:資料集名稱,其中包含您要更新的資料表。
    • TABLE:要更新的資料表名稱。
    • SCHEMA:寫入本機電腦的結構定義定義檔。

    例如,輸入以下指令來更新預設專案中 mydataset.mytable 的結構定義。您本機機器上的 JSON 結構定義檔路徑為 /tmp/myschema.json。

    bq update mydataset.mytable /tmp/myschema.json
    

API

呼叫 tables.patch 方法,並使用 schema 屬性將空白資料欄新增至結構定義。由於 tables.update 方法會取代整個資料表資源,因此建議使用 tables.patch 方法。

Go

在試用這個範例之前,請先按照「使用用戶端程式庫的 BigQuery 快速入門導覽課程」中的 Go 設定操作說明進行操作。詳情請參閱 BigQuery Go API 參考文件。

如要向 BigQuery 進行驗證,請設定應用程式預設憑證。詳情請參閱「設定用戶端程式庫的驗證機制」。

import (
	"context"
	"fmt"

	"cloud.google.com/go/bigquery"
)

// updateTableAddColumn demonstrates modifying the schema of a table to append an additional column.
func updateTableAddColumn(projectID, datasetID, tableID string) error {
	// projectID := "my-project-id"
	// datasetID := "mydataset"
	// tableID := "mytable"
	ctx := context.Background()
	client, err := bigquery.NewClient(ctx, projectID)
	if err != nil {
		return fmt.Errorf("bigquery.NewClient: %v", err)
	}
	defer client.Close()

	tableRef := client.Dataset(datasetID).Table(tableID)
	meta, err := tableRef.Metadata(ctx)
	if err != nil {
		return err
	}
	newSchema := append(meta.Schema,
		&bigquery.FieldSchema{Name: "phone", Type: bigquery.StringFieldType},
	)
	update := bigquery.TableMetadataToUpdate{
		Schema: newSchema,
	}
	if _, err := tableRef.Update(ctx, update, meta.ETag); err != nil {
		return err
	}
	return nil
}

Java

在試用這個範例之前,請先按照「使用用戶端程式庫的 BigQuery 快速入門導覽課程」中的 Java 設定操作說明進行操作。詳情請參閱 BigQuery Java API 參考文件。

如要向 BigQuery 進行驗證,請設定應用程式預設憑證。詳情請參閱「設定用戶端程式庫的驗證機制」。

import com.google.cloud.bigquery.BigQuery;
import com.google.cloud.bigquery.BigQueryException;
import com.google.cloud.bigquery.BigQueryOptions;
import com.google.cloud.bigquery.Field;
import com.google.cloud.bigquery.FieldList;
import com.google.cloud.bigquery.LegacySQLTypeName;
import com.google.cloud.bigquery.Schema;
import com.google.cloud.bigquery.StandardTableDefinition;
import com.google.cloud.bigquery.Table;
import java.util.ArrayList;
import java.util.List;

public class AddEmptyColumn {

  public static void runAddEmptyColumn() {
    // TODO(developer): Replace these variables before running the sample.
    String datasetName = "MY_DATASET_NAME";
    String tableId = "MY_TABLE_NAME";
    String newColumnName = "NEW_COLUMN_NAME";
    addEmptyColumn(newColumnName, datasetName, tableId);
  }

  public static void addEmptyColumn(String newColumnName, String datasetName, String tableId) {
    try {
      // Initialize client that will be used to send requests. This client only needs to be created
      // once, and can be reused for multiple requests.
      BigQuery bigquery = BigQueryOptions.getDefaultInstance().getService();

      Table table = bigquery.getTable(datasetName, tableId);
      Schema schema = table.getDefinition().getSchema();
      FieldList fields = schema.getFields();

      // Create the new field/column
      Field newField = Field.of(newColumnName, LegacySQLTypeName.STRING);

      // Create a new schema adding the current fields, plus the new one
      List<Field> fieldList = new ArrayList<Field>();
      fields.forEach(fieldList::add);
      fieldList.add(newField);
      Schema newSchema = Schema.of(fieldList);

      // Update the table with the new schema
      Table updatedTable =
          table.toBuilder().setDefinition(StandardTableDefinition.of(newSchema)).build();
      updatedTable.update();
      System.out.println("Empty column successfully added to table");
    } catch (BigQueryException e) {
      System.out.println("Empty column was not added. \n" + e.toString());
    }
  }
}

Node.js

在試用這個範例之前,請先按照「使用用戶端程式庫的 BigQuery 快速入門導覽課程」中的 Node.js 設定操作說明進行操作。詳情請參閱 BigQuery Node.js API 參考文件。

如要向 BigQuery 進行驗證,請設定應用程式預設憑證。詳情請參閱「設定用戶端程式庫的驗證機制」。


// Import the Google Cloud client library and create a client
const {BigQuery} = require('@google-cloud/bigquery');
const bigquery = new BigQuery();

async function addEmptyColumn() {
  // Adds an empty column to the schema.

  /**
   * TODO(developer): Uncomment the following lines before running the sample.
   */
  // const datasetId = 'my_dataset';
  // const tableId = 'my_table';
  const column = {name: 'size', type: 'STRING'};

  // Retrieve current table metadata
  const table = bigquery.dataset(datasetId).table(tableId);
  const [metadata] = await table.getMetadata();

  // Update table schema
  const schema = metadata.schema;
  const new_schema = schema;
  new_schema.fields.push(column);
  metadata.schema = new_schema;

  const [result] = await table.setMetadata(metadata);
  console.log(result.schema.fields);
}

Python

在試用這個範例之前,請先按照「使用用戶端程式庫的 BigQuery 快速入門導覽課程」中的 Python 設定操作說明進行操作。詳情請參閱 BigQuery Python API 參考文件。

如要向 BigQuery 進行驗證,請設定應用程式預設憑證。詳情請參閱「設定用戶端程式庫的驗證機制」。

將新的 SchemaField 物件附加至 Table.schema 的副本,然後將 Table.schema 屬性的值替換為更新後的結構定義。
from google.cloud import bigquery

# Construct a BigQuery client object.
client = bigquery.Client()

# TODO(developer): Set table_id to the ID of the table
#                  to add an empty column.
# table_id = "your-project.your_dataset.your_table_name"

table = client.get_table(table_id)  # Make an API request.

original_schema = table.schema
new_schema = original_schema[:]  # Creates a copy of the schema.
new_schema.append(bigquery.SchemaField("phone", "STRING"))

table.schema = new_schema
table = client.update_table(table, ["schema"])  # Make an API request.

if len(table.schema) == len(original_schema) + 1 == len(new_schema):
    print("A new column has been added.")
else:
    print("The column has not been added.")

在 RECORD 欄中新增巢狀欄

除了在資料表的結構定義中新增資料欄,您也可以在 RECORD 資料欄中新增巢狀資料欄。新增巢狀資料欄的程序與新增資料欄類似。

控制台

Google Cloud 控制台不支援在現有RECORD資料欄中新增巢狀欄位。

SQL

不支援使用 SQL DDL 陳述式,在現有 RECORD 資料欄中新增巢狀欄位。

bq

發出 bq update 指令並提供 JSON 結構定義檔,該檔案會將巢狀欄位新增至現有 RECORD 資料欄的結構定義。如果您要更新的資料表位於預設專案以外的專案,請按照下列格式將專案 ID 新增至資料集名稱:PROJECT_ID:DATASET。

bq update PROJECT_ID:DATASET.TABLE SCHEMA

更改下列內容:

  • PROJECT_ID:您的專案 ID。
  • DATASET:資料集名稱,其中包含您要更新的資料表。
  • TABLE:要更新的資料表名稱。
  • SCHEMA:本機電腦上 JSON 結構定義檔案的路徑。

指定內嵌結構定義時,無法指定資料欄說明、模式和 RECORD (STRUCT) 類型。所有資料欄模式預設為 NULLABLE。因此如要新增巢狀資料欄至 RECORD,就必須提供 JSON 結構定義檔。

若要使用 JSON 結構檔案將巢狀欄位新增至 RECORD,請進行以下操作:

  1. 首先,請發出 bq show 指令並加上 --schema 旗標,並將現有的資料表結構定義寫入檔案。如果您要更新的資料表位於預設專案以外的專案,請按照下列格式,將專案 ID 新增至資料集名稱:PROJECT_ID:DATASET.TABLE。

    bq show \
    --schema \
    --format=prettyjson \
    PROJECT_ID:DATASET.TABLE > SCHEMA

    更改下列內容:

    • PROJECT_ID:您的專案 ID。
    • DATASET:資料集名稱,其中包含您要更新的資料表。
    • TABLE:要更新的資料表名稱。
    • SCHEMA:寫入本機電腦的結構定義定義檔。

    舉例來說,如要將 mydataset.mytable 的結構定義寫入檔案,請輸入下列指令。mydataset.mytable 位於您的預設專案中。

    bq show \
    --schema \
    --format=prettyjson \
    mydataset.mytable > /tmp/myschema.json
    
  2. 在文字編輯器中開啟結構定義檔。結構定義應如下所示。在此範例中,column3 是巢狀的重複資料欄。巢狀資料欄為 nested1 和 nested2。fields 陣列列出了以巢狀形式嵌套在 column3 中的欄位。

    [
      {
        "mode": "REQUIRED",
        "name": "column1",
        "type": "STRING"
      },
      {
        "mode": "REQUIRED",
        "name": "column2",
        "type": "FLOAT"
      },
      {
        "fields": [
          {
            "mode": "NULLABLE",
            "name": "nested1",
            "type": "STRING"
          },
          {
            "mode": "NULLABLE",
            "name": "nested2",
            "type": "STRING"
          }
        ],
        "mode": "REPEATED",
        "name": "column3",
        "type": "RECORD"
      }
    ]
    
  3. 在 fields 陣列的結尾處新增巢狀資料欄。巢狀欄位一律會新增至欄位結尾。在這個範例中,nested3 是新的巢狀資料欄。

      [
        {
          "mode": "REQUIRED",
          "name": "column1",
          "type": "STRING"
        },
        {
          "mode": "REQUIRED",
          "name": "column2",
          "type": "FLOAT"
        },
        {
          "fields": [
            {
              "mode": "NULLABLE",
              "name": "nested1",
              "type": "STRING"
            },
            {
              "mode": "NULLABLE",
              "name": "nested2",
              "type": "STRING"
            },
            {
              "mode": "NULLABLE",
              "name": "nested3",
              "type": "STRING"
            }
          ],
          "mode": "REPEATED",
          "name": "column3",
          "type": "RECORD"
        }
      ]
      

    如要進一步瞭解如何使用 JSON 結構定義檔,請參閱指定 JSON 結構定義檔一節。

  4. 更新結構定義檔後,請發出下列指令來更新資料表的結構定義。如果要更新的資料表位於預設專案以外的專案,請按照下列格式,將專案 ID 新增至資料集名稱:PROJECT_ID:DATASET。

    bq update PROJECT_ID:DATASET.TABLE SCHEMA

    更改下列內容:

    • PROJECT_ID:您的專案 ID。
    • DATASET:資料集名稱,其中包含您要更新的資料表。
    • TABLE:要更新的資料表名稱。
    • SCHEMA:本機電腦上的 JSON 結構定義檔案路徑。

    例如,輸入以下指令來更新預設專案中 mydataset.mytable 的結構定義。您本機機器上的 JSON 結構定義檔路徑為 /tmp/myschema.json。

    bq update mydataset.mytable /tmp/myschema.json
    

API

呼叫 tables.patch 方法,並使用 schema 屬性將巢狀資料欄新增至結構定義。由於 tables.update 方法會取代整個資料表資源,因此建議使用 tables.patch 方法。

覆寫或附加資料時新增資料欄

將資料載入現有資料表並選擇覆寫現有資料表時,可以新增資料欄至現有資料表。覆寫現有的資料表時,系統會使用所載入資料的結構定義來覆寫現有資料表的結構定義。如要瞭解如何使用載入工作覆寫資料表,請參閱資料格式適用的文件:

在載入附加工作中新增資料欄

在載入工作中將資料附加至資料表時,您可以將資料欄新增至資料表。新結構定義由下列其中一項決定:

  • 自動偵測 (適用於 CSV 和 JSON 檔案)
  • 在 JSON 結構定義檔案中指定的結構定義 (適用於 CSV 和 JSON 檔案)
  • Avro、ORC、Parquet 和 Datastore 匯出檔案的自述式來源資料

若您在 JSON 檔中指定結構,則必須在其中定義新的資料欄。如果缺少新的資料欄定義,系統會在您嘗試附加資料時傳回錯誤。

當您在附加作業期間新增資料欄時,系統將會針對現有資料列將新資料欄中的值設為 NULL。

如要在載入工作期間將資料附加至資料表時新增資料欄,請使用下列其中一個選項:

bq

請使用 bq load 指令載入資料並指定 --noreplace 旗標,以指出您要將資料附加至現有的資料表。

如果您要附加的資料是 CSV 格式,或是以換行符號分隔的 JSON 格式,請指定 --autodetect 旗標以使用結構定義自動偵測功能,或在 JSON 結構定義檔中提供結構定義。系統可從 Avro 或 Datastore 匯出檔自動推斷新增的資料欄。

將 --schema_update_option 旗標設為 ALLOW_FIELD_ADDITION,以指出要附加的資料中包含新的資料欄。

如果您要附加的資料表位於預設專案以外的資料集,請按照下列格式,將專案 ID 新增至資料集名稱:PROJECT_ID:DATASET。

(選用) 提供 --location 旗標,並將值設為您的位置。

輸入 load 指令,如下所示:

bq --location=LOCATION load \
--noreplace \
--autodetect \
--schema_update_option=ALLOW_FIELD_ADDITION \
--source_format=FORMAT \
PROJECT_ID:DATASET.TABLE \
PATH_TO_SOURCE \
SCHEMA

更改下列內容:

  • LOCATION:位置名稱。--location 是選用旗標。舉例來說,如果您在東京地區使用 BigQuery,就可將該旗標的值設為 asia-northeast1。您可以使用 .bigqueryrc 檔來設定位置的預設值。
  • FORMAT:結構定義的格式。NEWLINE_DELIMITED_JSON、CSV、AVRO、PARQUET、ORC 或 DATASTORE_BACKUP。
  • PROJECT_ID:您的專案 ID。
  • DATASET:資料集名稱,其中包含您要更新的資料表。
  • TABLE:要附加的資料表名稱。
  • PATH_TO_SOURCE:完整的 Cloud Storage URI、以半形逗號分隔的 URI 清單,或本機電腦上的資料檔案路徑。
  • SCHEMA:本機 JSON 結構定義檔案的路徑。不指定 --autodetect 時,只有 CSV 和 JSON 檔案需要結構定義檔。系統會從來源資料推斷 Avro 和 Datastore 的結構定義。

範例:

輸入下列指令,使用載入工作將本機 Avro 資料檔案 (/tmp/mydata.avro) 附加至 mydataset.mytable。由於系統會自動從 Avro 資料推論結構定義,因此您不需要使用 --autodetect 旗標。mydataset 位於您的預設專案中。

bq load \
--noreplace \
--schema_update_option=ALLOW_FIELD_ADDITION \
--source_format=AVRO \
mydataset.mytable \
/tmp/mydata.avro

輸入下列指令,使用載入工作將 Cloud Storage 中採用換行符號分隔格式的 JSON 資料檔案附加至 mydataset.mytable。--autodetect 旗標可用於偵測新資料欄。mydataset 位於您的預設專案中。

bq load \
--noreplace \
--autodetect \
--schema_update_option=ALLOW_FIELD_ADDITION \
--source_format=NEWLINE_DELIMITED_JSON \
mydataset.mytable \
gs://mybucket/mydata.json

輸入下列指令,使用載入工作將 Cloud Storage 中採用換行符號分隔格式的 JSON 資料檔案附加至 mydataset.mytable。包含新資料欄的結構定義是在本機 JSON 結構定義檔 /tmp/myschema.json 中指定。mydataset 在 myotherproject 中,而不在您的預設專案中。

bq load \
--noreplace \
--schema_update_option=ALLOW_FIELD_ADDITION \
--source_format=NEWLINE_DELIMITED_JSON \
myotherproject:mydataset.mytable \
gs://mybucket/mydata.json \
/tmp/myschema.json

API

呼叫 jobs.insert 方法。設定 load 工作和下列屬性:

  • 使用 sourceUris 屬性參照 Cloud Storage 中的資料。
  • 設定 sourceFormat 屬性來指定資料格式。
  • 在 schema 屬性中指定結構定義。
  • 使用 schemaUpdateOptions 屬性指定結構定義更新選項。
  • 使用 writeDisposition 屬性將目的地資料表的寫入處理方式設為 WRITE_APPEND。

Go

在試用這個範例之前,請先按照「使用用戶端程式庫的 BigQuery 快速入門導覽課程」中的 Go 設定操作說明進行操作。詳情請參閱 BigQuery Go API 參考文件。

如要向 BigQuery 進行驗證,請設定應用程式預設憑證。詳情請參閱「設定用戶端程式庫的驗證機制」。

import (
	"context"
	"fmt"
	"os"

	"cloud.google.com/go/bigquery"
)

// createTableAndWidenLoad demonstrates augmenting a table's schema to add a new column via a load job.
func createTableAndWidenLoad(projectID, datasetID, tableID, filename string) error {
	// projectID := "my-project-id"
	// datasetID := "mydataset"
	// tableID := "mytable"
	ctx := context.Background()
	client, err := bigquery.NewClient(ctx, projectID)
	if err != nil {
		return fmt.Errorf("bigquery.NewClient: %v", err)
	}
	defer client.Close()

	sampleSchema := bigquery.Schema{
		{Name: "full_name", Type: bigquery.StringFieldType},
	}
	meta := &bigquery.TableMetadata{
		Schema: sampleSchema,
	}
	tableRef := client.Dataset(datasetID).Table(tableID)
	if err := tableRef.Create(ctx, meta); err != nil {
		return err
	}
	// Now, import data from a local file, but specify field additions are allowed.
	// Because the data has a second column (age), the schema is amended as part of
	// the load.
	f, err := os.Open(filename)
	if err != nil {
		return err
	}
	source := bigquery.NewReaderSource(f)
	source.AutoDetect = true   // Allow BigQuery to determine schema.
	source.SkipLeadingRows = 1 // CSV has a single header line.

	loader := client.Dataset(datasetID).Table(tableID).LoaderFrom(source)
	loader.SchemaUpdateOptions = []string{"ALLOW_FIELD_ADDITION"}
	job, err := loader.Run(ctx)
	if err != nil {
		return err
	}
	status, err := job.Wait(ctx)
	if err != nil {
		return err
	}
	if err := status.Err(); err != nil {
		return err
	}
	return nil
}

Java

在試用這個範例之前,請先按照「使用用戶端程式庫的 BigQuery 快速入門導覽課程」中的 Java 設定操作說明進行操作。詳情請參閱 BigQuery Java API 參考文件。

如要向 BigQuery 進行驗證,請設定應用程式預設憑證。詳情請參閱「設定用戶端程式庫的驗證機制」。

import com.google.cloud.bigquery.BigQuery;
import com.google.cloud.bigquery.BigQueryException;
import com.google.cloud.bigquery.BigQueryOptions;
import com.google.cloud.bigquery.Field;
import com.google.cloud.bigquery.FormatOptions;
import com.google.cloud.bigquery.Job;
import com.google.cloud.bigquery.JobId;
import com.google.cloud.bigquery.JobInfo;
import com.google.cloud.bigquery.JobInfo.SchemaUpdateOption;
import com.google.cloud.bigquery.JobInfo.WriteDisposition;
import com.google.cloud.bigquery.LegacySQLTypeName;
import com.google.cloud.bigquery.LoadJobConfiguration;
import com.google.cloud.bigquery.Schema;
import com.google.cloud.bigquery.TableId;
import com.google.common.collect.ImmutableList;
import java.util.UUID;

public class AddColumnLoadAppend {

  public static void runAddColumnLoadAppend() throws Exception {
    // TODO(developer): Replace these variables before running the sample.
    String datasetName = "MY_DATASET_NAME";
    String tableName = "MY_TABLE_NAME";
    String sourceUri = "/path/to/file.csv";
    addColumnLoadAppend(datasetName, tableName, sourceUri);
  }

  public static void addColumnLoadAppend(String datasetName, String tableName, String sourceUri)
      throws Exception {
    try {
      // Initialize client that will be used to send requests. This client only needs to be created
      // once, and can be reused for multiple requests.
      BigQuery bigquery = BigQueryOptions.getDefaultInstance().getService();

      TableId tableId = TableId.of(datasetName, tableName);

      // Add a new column to a BigQuery table while appending rows via a load job.
      // 'REQUIRED' fields cannot  be added to an existing schema, so the additional column must be
      // 'NULLABLE'.
      Schema newSchema =
          Schema.of(
              Field.newBuilder("name", LegacySQLTypeName.STRING)
                  .setMode(Field.Mode.REQUIRED)
                  .build(),
              // Adding below additional column during the load job
              Field.newBuilder("post_abbr", LegacySQLTypeName.STRING)
                  .setMode(Field.Mode.NULLABLE)
                  .build());

      LoadJobConfiguration loadJobConfig =
          LoadJobConfiguration.builder(tableId, sourceUri)
              .setFormatOptions(FormatOptions.csv())
              .setWriteDisposition(WriteDisposition.WRITE_APPEND)
              .setSchema(newSchema)
              .setSchemaUpdateOptions(ImmutableList.of(SchemaUpdateOption.ALLOW_FIELD_ADDITION))
              .build();

      // Create a job ID so that we can safely retry.
      JobId jobId = JobId.of(UUID.randomUUID().toString());
      Job loadJob = bigquery.create(JobInfo.newBuilder(loadJobConfig).setJobId(jobId).build());

      // Load data from a GCS parquet file into the table
      // Blocks until this load table job completes its execution, either failing or succeeding.
      Job completedJob = loadJob.waitFor();

      // Check for errors
      if (completedJob == null) {
        throw new Exception("Job not executed since it no longer exists.");
      } else if (completedJob.getStatus().getError() != null) {
        // You can also look at queryJob.getStatus().getExecutionErrors() for all
        // errors, not just the latest one.
        throw new Exception(
            "BigQuery was unable to load into the table due to an error: \n"
                + loadJob.getStatus().getError());
      }
      System.out.println("Column successfully added during load append job");
    } catch (BigQueryException | InterruptedException e) {
      System.out.println("Column not added during load append \n" + e.toString());
    }
  }
}

Node.js

在試用這個範例之前,請先按照「使用用戶端程式庫的 BigQuery 快速入門導覽課程」中的 Node.js 設定操作說明進行操作。詳情請參閱 BigQuery Node.js API 參考文件。

如要向 BigQuery 進行驗證,請設定應用程式預設憑證。詳情請參閱「設定用戶端程式庫的驗證機制」。

// Import the Google Cloud client libraries
const {BigQuery} = require('@google-cloud/bigquery');

// Instantiate client
const bigquery = new BigQuery();

async function addColumnLoadAppend() {
  // Adds a new column to a BigQuery table while appending rows via a load job.

  /**
   * TODO(developer): Uncomment the following lines before running the sample.
   */
  // const fileName = '/path/to/file.csv';
  // const datasetId = 'my_dataset';
  // const tableId = 'my_table';

  // In this example, the existing table contains only the 'Name', 'Age',
  // & 'Weight' columns. 'REQUIRED' fields cannot  be added to an existing
  // schema, so the additional column must be 'NULLABLE'.
  const schema = 'Name:STRING, Age:INTEGER, Weight:FLOAT, IsMagic:BOOLEAN';

  // Retrieve destination table reference
  const [table] = await bigquery
    .dataset(datasetId)
    .table(tableId)
    .get();
  const destinationTableRef = table.metadata.tableReference;

  // Set load job options
  const options = {
    schema: schema,
    schemaUpdateOptions: ['ALLOW_FIELD_ADDITION'],
    writeDisposition: 'WRITE_APPEND',
    destinationTable: destinationTableRef,
  };

  // Load data from a local file into the table
  const [job] = await bigquery
    .dataset(datasetId)
    .table(tableId)
    .load(fileName, options);

  console.log(`Job ${job.id} completed.`);
  console.log(`New Schema:`);
  console.log(job.configuration.load.schema.fields);

  // Check the job's status for errors
  const errors = job.status.errors;
  if (errors && errors.length > 0) {
    throw errors;
  }
}

Python

在試用這個範例之前,請先按照「使用用戶端程式庫的 BigQuery 快速入門導覽課程」中的 Python 設定操作說明進行操作。詳情請參閱 BigQuery Python API 參考文件。

如要向 BigQuery 進行驗證,請設定應用程式預設憑證。詳情請參閱「設定用戶端程式庫的驗證機制」。

# from google.cloud import bigquery
# client = bigquery.Client()
# project = client.project
# dataset_ref = bigquery.DatasetReference(project, 'my_dataset')
# filepath = 'path/to/your_file.csv'

# Retrieves the destination table and checks the length of the schema
table_id = "my_table"
table_ref = dataset_ref.table(table_id)
table = client.get_table(table_ref)
print("Table {} contains {} columns.".format(table_id, len(table.schema)))

# Configures the load job to append the data to the destination table,
# allowing field addition
job_config = bigquery.LoadJobConfig()
job_config.write_disposition = bigquery.WriteDisposition.WRITE_APPEND
job_config.schema_update_options = [
    bigquery.SchemaUpdateOption.ALLOW_FIELD_ADDITION
]
# In this example, the existing table contains only the 'full_name' column.
# 'REQUIRED' fields cannot be added to an existing schema, so the
# additional column must be 'NULLABLE'.
job_config.schema = [
    bigquery.SchemaField("full_name", "STRING", mode="REQUIRED"),
    bigquery.SchemaField("age", "INTEGER", mode="NULLABLE"),
]
job_config.source_format = bigquery.SourceFormat.CSV
job_config.skip_leading_rows = 1

with open(filepath, "rb") as source_file:
    job = client.load_table_from_file(
        source_file,
        table_ref,
        location="US",  # Must match the destination dataset location.
        job_config=job_config,
    )  # API request

job.result()  # Waits for table load to complete.
print(
    "Loaded {} rows into {}:{}.".format(
        job.output_rows, dataset_id, table_ref.table_id
    )
)

# Checks the updated length of the schema
table = client.get_table(table)
print("Table {} now contains {} columns.".format(table_id, len(table.schema)))

在查詢附加工作中新增資料欄

將查詢結果附加至資料表時,可以新增資料欄。

在查詢工作中使用附加作業新增資料欄時,系統會使用查詢結果的結構定義來更新目的地資料表的結構定義。請注意,您無法在一個位置查詢資料表後,將結果寫入不同位置的資料表。

如要在查詢工作期間將資料附加至資料表時新增資料欄,請選取下列其中一個選項:

bq

使用 bq query 指令查詢資料,並指定 --destination_table 旗標來指出您要附加的資料表。

如要指定將查詢結果附加至現有目的地資料表,請指定 --append_table 旗標。

將 --schema_update_option 旗標設定為 ALLOW_FIELD_ADDITION,以指出要附加的查詢結果中包含新的資料欄。

指定 use_legacy_sql=false 旗標,以便在查詢中使用 GoogleSQL 語法。

如果您要附加的資料表位於預設專案以外的資料集,請按照下列格式將專案 ID 新增至資料集名稱:PROJECT_ID:DATASET。請注意,您查詢的資料表和目的地資料表必須位於相同位置。

(選用) 提供 --location 旗標,並將值設為您的位置。

bq --location=LOCATION query \
--destination_table PROJECT_ID:DATASET.TABLE \
--append_table \
--schema_update_option=ALLOW_FIELD_ADDITION \
--use_legacy_sql=false \
'QUERY'

更改下列內容:

  • LOCATION:位置名稱。--location 是選用旗標。舉例來說,如果您在東京地區使用 BigQuery,就可將該旗標的值設為 asia-northeast1。您可以使用 .bigqueryrc 檔來設定位置的預設值。請注意,您無法將查詢結果附加至不同位置的資料表。
  • PROJECT_ID:您的專案 ID。
  • dataset:包含要附加資料表的資料集名稱。
  • TABLE:要附加的資料表名稱。
  • QUERY:GoogleSQL 語法的查詢。

範例:

輸入下列指令,以查詢預設專案中的 mydataset.mytable,並將查詢結果附加至 mydataset.mytable2 (也在預設專案中)。

bq query \
--destination_table mydataset.mytable2 \
--append_table \
--schema_update_option=ALLOW_FIELD_ADDITION \
--use_legacy_sql=false \
'SELECT
   column1,column2
 FROM
   mydataset.mytable'

輸入下列指令,以查詢預設專案中的 mydataset.mytable,並將查詢結果附加至位於 myotherproject 中的 mydataset.mytable2。

bq query \
--destination_table myotherproject:mydataset.mytable2 \
--append_table \
--schema_update_option=ALLOW_FIELD_ADDITION \
--use_legacy_sql=false \
'SELECT
   column1,column2
 FROM
   mydataset.mytable'

API

呼叫 jobs.insert 方法。設定 query 工作和下列屬性:

  • 使用 destinationTable 屬性指定目的地資料表。
  • 使用 writeDisposition 屬性將目的地資料表的寫入處理方式設為 WRITE_APPEND。
  • 使用 schemaUpdateOptions 屬性指定結構定義更新選項。
  • 使用 query 屬性指定 GoogleSQL 查詢。

Go

在試用這個範例之前,請先按照「使用用戶端程式庫的 BigQuery 快速入門導覽課程」中的 Go 設定操作說明進行操作。詳情請參閱 BigQuery Go API 參考文件。

如要向 BigQuery 進行驗證,請設定應用程式預設憑證。詳情請參閱「設定用戶端程式庫的驗證機制」。

import (
	"context"
	"fmt"

	"cloud.google.com/go/bigquery"
)

// createTableAndWidenQuery demonstrates how the schema of a table can be modified to add columns by appending
// query results that include the new columns.
func createTableAndWidenQuery(projectID, datasetID, tableID string) error {
	// projectID := "my-project-id"
	// datasetID := "mydataset"
	// tableID := "mytable"
	ctx := context.Background()
	client, err := bigquery.NewClient(ctx, projectID)
	if err != nil {
		return fmt.Errorf("bigquery.NewClient: %v", err)
	}
	defer client.Close()

	// First, we create a sample table.
	sampleSchema := bigquery.Schema{
		{Name: "full_name", Type: bigquery.StringFieldType, Required: true},
		{Name: "age", Type: bigquery.IntegerFieldType, Required: true},
	}
	original := &bigquery.TableMetadata{
		Schema: sampleSchema,
	}
	tableRef := client.Dataset(datasetID).Table(tableID)
	if err := tableRef.Create(ctx, original); err != nil {
		return err
	}
	// Our table has two columns.  We'll introduce a new favorite_color column via
	// a subsequent query that appends to the table.
	q := client.Query("SELECT \"Timmy\" as full_name, 85 as age, \"Blue\" as favorite_color")
	q.SchemaUpdateOptions = []string{"ALLOW_FIELD_ADDITION"}
	q.QueryConfig.Dst = client.Dataset(datasetID).Table(tableID)
	q.WriteDisposition = bigquery.WriteAppend
	q.Location = "US"
	job, err := q.Run(ctx)
	if err != nil {
		return err
	}
	_, err = job.Wait(ctx)
	if err != nil {
		return err
	}
	return nil
}

Java

在試用這個範例之前,請先按照「使用用戶端程式庫的 BigQuery 快速入門導覽課程」中的 Java 設定操作說明進行操作。詳情請參閱 BigQuery Java API 參考文件。

如要向 BigQuery 進行驗證,請設定應用程式預設憑證。詳情請參閱「設定用戶端程式庫的驗證機制」。

import com.google.cloud.bigquery.BigQuery;
import com.google.cloud.bigquery.BigQueryException;
import com.google.cloud.bigquery.BigQueryOptions;
import com.google.cloud.bigquery.Job;
import com.google.cloud.bigquery.JobInfo;
import com.google.cloud.bigquery.JobInfo.SchemaUpdateOption;
import com.google.cloud.bigquery.JobInfo.WriteDisposition;
import com.google.cloud.bigquery.QueryJobConfiguration;
import com.google.cloud.bigquery.TableId;
import com.google.cloud.bigquery.TableResult;
import com.google.common.collect.ImmutableList;

public class RelaxTableQuery {

  public static void runRelaxTableQuery() throws Exception {
    // TODO(developer): Replace these variables before running the sample.
    String projectId = "MY_PROJECT_ID";
    String datasetName = "MY_DATASET_NAME";
    String tableName = "MY_TABLE_NAME";
    relaxTableQuery(projectId, datasetName, tableName);
  }

  // To relax all columns in a destination table when you append data to it during a query job
  public static void relaxTableQuery(String projectId, String datasetName, String tableName)
      throws Exception {
    try {
      // Initialize client that will be used to send requests. This client only needs to be created
      // once, and can be reused for multiple requests.
      BigQuery bigquery = BigQueryOptions.getDefaultInstance().getService();

      TableId tableId = TableId.of(datasetName, tableName);

      String sourceTable = "`" + projectId + "." + datasetName + "." + tableName + "`";
      String query = "SELECT word FROM " + sourceTable + " WHERE word like '%is%'";

      QueryJobConfiguration queryConfig =
          QueryJobConfiguration.newBuilder(query)
              // Use standard SQL syntax for queries.
              // See: https://cloud.google.com/bigquery/sql-reference/
              .setUseLegacySql(false)
              .setSchemaUpdateOptions(ImmutableList.of(SchemaUpdateOption.ALLOW_FIELD_RELAXATION))
              .setWriteDisposition(WriteDisposition.WRITE_APPEND)
              .setDestinationTable(tableId)
              .build();

      Job queryJob = bigquery.create(JobInfo.newBuilder(queryConfig).build());

      queryJob = queryJob.waitFor();

      // Check for errors
      if (queryJob == null) {
        throw new Exception("Job no longer exists");
      } else if (queryJob.getStatus().getError() != null) {
        // You can also look at queryJob.getStatus().getExecutionErrors() for all
        // errors, not just the latest one.
        throw new Exception(queryJob.getStatus().getError().toString());
      }

      // Get the results.
      TableResult results = queryJob.getQueryResults();

      // Print all pages of the results.
      results
          .iterateAll()
          .forEach(
              rows -> {
                rows.forEach(row -> System.out.println("row: " + row.toString()));
              });

      System.out.println("Successfully relaxed all columns in destination table during query job");
    } catch (BigQueryException | InterruptedException e) {
      System.out.println("Columns not relaxed during query job \n" + e.toString());
    }
  }
}

Node.js

在試用這個範例之前,請先按照「使用用戶端程式庫的 BigQuery 快速入門導覽課程」中的 Node.js 設定操作說明進行操作。詳情請參閱 BigQuery Node.js API 參考文件。

如要向 BigQuery 進行驗證,請設定應用程式預設憑證。詳情請參閱「設定用戶端程式庫的驗證機制」。

// Import the Google Cloud client libraries
const {BigQuery} = require('@google-cloud/bigquery');

// Instantiate client
const bigquery = new BigQuery();

async function addColumnQueryAppend() {
  // Adds a new column to a BigQuery table while appending rows via a query job.

  /**
   * TODO(developer): Uncomment the following lines before running the sample.
   */
  // const datasetId = 'my_dataset';
  // const tableId = 'my_table';

  // Retrieve destination table reference
  const [table] = await bigquery
    .dataset(datasetId)
    .table(tableId)
    .get();
  const destinationTableRef = table.metadata.tableReference;

  // In this example, the existing table contains only the 'name' column.
  // 'REQUIRED' fields cannot  be added to an existing schema,
  // so the additional column must be 'NULLABLE'.
  const query = `SELECT name, year
    FROM \`bigquery-public-data.usa_names.usa_1910_2013\`
    WHERE state = 'TX'
    LIMIT 10`;

  // Set load job options
  const options = {
    query: query,
    schemaUpdateOptions: ['ALLOW_FIELD_ADDITION'],
    writeDisposition: 'WRITE_APPEND',
    destinationTable: destinationTableRef,
    // Location must match that of the dataset(s) referenced in the query.
    location: 'US',
  };

  const [job] = await bigquery.createQueryJob(options);
  console.log(`Job ${job.id} started.`);

  // Wait for the query to finish
  const [rows] = await job.getQueryResults();
  console.log(`Job ${job.id} completed.`);

  // Print the results
  console.log('Rows:');
  rows.forEach(row => console.log(row));
}

Python

在試用這個範例之前,請先按照「使用用戶端程式庫的 BigQuery 快速入門導覽課程」中的 Python 設定操作說明進行操作。詳情請參閱 BigQuery Python API 參考文件。

如要向 BigQuery 進行驗證,請設定應用程式預設憑證。詳情請參閱「設定用戶端程式庫的驗證機制」。

from google.cloud import bigquery

# Construct a BigQuery client object.
client = bigquery.Client()

# TODO(developer): Set table_id to the ID of the destination table.
# table_id = "your-project.your_dataset.your_table_name"

# Retrieves the destination table and checks the length of the schema.
table = client.get_table(table_id)  # Make an API request.
print("Table {} contains {} columns".format(table_id, len(table.schema)))

# Configures the query to append the results to a destination table,
# allowing field addition.
job_config = bigquery.QueryJobConfig(
    destination=table_id,
    schema_update_options=[bigquery.SchemaUpdateOption.ALLOW_FIELD_ADDITION],
    write_disposition=bigquery.WriteDisposition.WRITE_APPEND,
)

# Start the query, passing in the extra configuration.
client.query_and_wait(
    # In this example, the existing table contains only the 'full_name' and
    # 'age' columns, while the results of this query will contain an
    # additional 'favorite_color' column.
    'SELECT "Timmy" as full_name, 85 as age, "Blue" as favorite_color;',
    job_config=job_config,
)  # Make an API request and wait for job to complete.

# Checks the updated length of the schema.
table = client.get_table(table_id)  # Make an API request.
print("Table {} now contains {} columns".format(table_id, len(table.schema)))

變更資料欄名稱

如要重新命名資料表中的資料欄,請使用 ALTER TABLE RENAME COLUMN DDL 陳述式。以下範例會將 mytable 的 old_name 資料欄重新命名為 new_name:

ALTER TABLE mydataset.mytable
  RENAME COLUMN old_name TO new_name;

如要進一步瞭解 ALTER TABLE RENAME COLUMN 陳述式,請參閱「DDL 詳細資料」。

變更資料欄的資料類型

Google Cloud 控制台、bq 指令列工具或 BigQuery API 不支援變更資料欄的資料類型。如果嘗試套用結構定義來更新資料表,但結構定義為資料欄指定了新的資料類型,系統會傳回錯誤。

使用 DDL 陳述式變更資料欄的資料類型

您可以使用 GoogleSQL 對資料欄的資料類型進行特定變更。如需更多資訊和支援的資料類型轉換完整清單,請參閱 ALTER COLUMN SET DATA TYPE DDL 陳述式。

以下範例會建立包含 INT64 類型資料欄的資料表,然後將類型更新為 NUMERIC:

CREATE TABLE mydataset.mytable(c1 INT64);

ALTER TABLE mydataset.mytable
ALTER COLUMN c1 SET DATA TYPE NUMERIC;

以下範例會建立一個資料表,其中包含一個具有兩個欄位的巢狀資料欄,然後將其中一個資料欄的類型從 INT 更新為 NUMERIC:

CREATE TABLE mydataset.mytable(s1 STRUCT<a INT64, b STRING>);

ALTER TABLE mydataset.mytable ALTER COLUMN s1
SET DATA TYPE STRUCT<a NUMERIC, b STRING>;

修改巢狀資料欄類型

如果是複雜的巢狀結構定義變更,例如變更 STRUCT 陣列中的欄位,則不支援 ALTER TABLE DDL 陳述式。如要解決這個問題,您可以使用 CREATE OR REPLACE TABLE 陳述式和 SELECT 陳述式,轉換巢狀結構定義變更。

以下範例示範如何轉換 STRUCTS 陣列中的資料欄:

假設資料表 samples.test 具有下列結構定義和資料:

CREATE OR REPLACE TABLE
  samples.test(D STRUCT <L ARRAY<STRUCT<R STRING, U STRING, V STRING>>, F STRING>);

INSERT INTO
  samples.test(D)
VALUES
  (STRUCT([STRUCT("r1", "u1", "v1"), STRUCT("r2", "u2", "v2")], "f1"));

傳回的結果看起來類似下列內容:

+----------------------------------------------------------------------------+
|                                     D                                      |
+----------------------------------------------------------------------------+
| {"L":[{"R":"r1","U":"u1","V":"v1"},{"R":"r2","U":"u2","V":"v2"}],"F":"f1"} |
+----------------------------------------------------------------------------+

假設您需要將 STRUCT 巢狀陣列中的 U 欄位類型變更為 STRUCT<W STRING>。下列 SQL 陳述式示範如何完成這項操作:

CREATE OR REPLACE TABLE
  samples.new_table AS
SELECT
  STRUCT(ARRAY(
    SELECT
      STRUCT(tmp.R,
        STRUCT(tmp.U AS W) AS U,
        tmp.V)
    FROM
      UNNEST(t.D.L) AS tmp) AS L,
    t.D.F) AS D
FROM
  samples.test AS t

這個陳述式會建立新資料表 samples.new_table,並套用目標結構定義。UNNEST 函式會展開 t.D.L 中的 STRUCT 陣列。運算式 STRUCT(tmp.U AS W) AS U 會建構新的 STRUCT,其中包含欄位 W,並填入原始 U 欄位的值。產生的資料表 samples.new_table 具有下列結構定義和資料:

+----------------------------------------------------------------------------------------+
|                                           D                                            |
+----------------------------------------------------------------------------------------+
| {"L":[{"R":"r1","U":{"W":"u1"},"V":"v1"},{"R":"r2","U":{"W":"u2"},"V":"v2"}],"F":"f1"} |
+----------------------------------------------------------------------------------------+

轉換資料欄的資料類型

如要將資料欄的資料類型變更為可轉換類型,請使用 SQL 查詢選取資料表資料、轉換相關資料欄,並覆寫資料表。不建議對大型資料表進行轉換和覆寫,因為這需要掃描整個資料表。

以下範例顯示的 SQL 查詢會從 mydataset.mytable 中的 column_two 和 column_three 選取所有資料,並將 column_one 從 DATE 轉換為 STRING。並使用查詢結果覆寫現有的資料表。覆寫的資料表會將 column_one 儲存為 STRING 資料類型。

使用 CAST 時,如果 BigQuery 無法執行類型轉換,查詢可能會失敗。如要進一步瞭解 GoogleSQL 的轉換規則,請參閱「轉換」。

控制台

  1. 前往 Google Cloud 控制台的「BigQuery」頁面。

    前往「BigQuery」

  2. 在「Query editor」(查詢編輯器) 中輸入以下查詢,藉此選取 mydataset.mytable 中 column_two 和 column_three 的所有資料,並將 column_one 由 DATE 轉換為 STRING。這項查詢會使用別名轉換具備相同名稱的 column_one。mydataset.mytable 在您的預設專案中。

    SELECT
     column_two,
     column_three,
     CAST(column_one AS STRING) AS column_one
    FROM
     mydataset.mytable;
  3. 依序點選「編輯」>「查詢設定」。

  4. 在「Destination」(目的地) 部分,執行下列操作:

    1. 選取 [Set a destination table for query results] (為查詢結果設定目標資料表) 核取方塊。

    2. 保留「Project name」(專案名稱) 欄位中預設專案的設定值 (含有 mydataset.mytable 的專案)。

    3. 在「Dataset」(資料集) 部分,選擇 mydataset。

    4. 在「Table Id」(資料表 ID) 欄位中輸入 mytable。

    5. 在「目的地資料表寫入偏好設定」部分,選取「覆寫資料表」。這個選項會使用查詢結果覆寫 mytable。

  5. 視需要選擇資料的位置。

  6. 如要更新設定,請按一下「儲存」。

  7. 按一下「 執行」。

    查詢工作完成後,column_one 的資料類型為 STRING。

bq

輸入以下 bq query 指令,藉此選取 mydataset.mytable 中 column_two 和 column_three 的所有資料,並將 column_one 由 DATE 轉換為 STRING。這項查詢會使用別名轉換具備相同名稱的 column_one。mydataset.mytable 在您的預設專案中。

查詢結果會使用 --destination_table 旗標寫入 mydataset.mytable,並使用 --replace 旗標覆寫 mytable。指定 use_legacy_sql=false 旗標,即可使用 GoogleSQL 語法。

視需要提供 --location 旗標,並將值設為您的位置。

bq query \
    --destination_table mydataset.mytable \
    --replace \
    --use_legacy_sql=false \
'SELECT
  column_two,
  column_three,
  CAST(column_one AS STRING) AS column_one
FROM
  mydataset.mytable'

API

如要選取 mydataset.mytable 中 column_two 和 column_three 的所有資料,並將 column_one 由 DATE 轉換為 STRING,請呼叫 jobs.insert 方法,並設定 query 工作。您也可以在 jobReference 區段的 location 屬性中指定位置。

查詢工作中使用的 SQL 查詢為 SELECT column_two, column_three, CAST(column_one AS STRING) AS column_one FROM mydataset.mytable。這項查詢會使用別名轉換具備相同名稱的 column_one。

如要使用查詢結果覆寫 mytable,請將 mydataset.mytable 加入 configuration.query.destinationTable 屬性,並在 configuration.query.writeDisposition 屬性中指定 WRITE_TRUNCATE。

變更資料欄模式

您只能將資料欄模式從 REQUIRED 變更為 NULLABLE,而將資料欄的模式從 REQUIRED 變更為 NULLABLE 也稱為資料欄放寬。您也可以在載入資料以覆寫現有資料表,或將資料附加至現有資料表時,放寬資料欄限制。您無法將資料欄模式從 NULLABLE 變更為 REQUIRED,或從 REPEATED 變更為 NULLABLE。如要將資料欄模式從 NULLABLE 變更為 REQUIRED,或從 REPEATED 變更為 NULLABLE,您必須使用更新後的資料欄模式重新建立資料表。

在現有資料表中將資料欄設為 NULLABLE

如要將資料欄的模式從 REQUIRED 變更為 NULLABLE,請選取下列其中一個選項:

控制台

  1. 前往「BigQuery」頁面。

    前往「BigQuery」

  2. 點選左側窗格中的 「Explorer」。

    醒目顯示的「Explorer」窗格按鈕。

  3. 在「Explorer」窗格中展開專案,點選「Datasets」(資料集),然後選取資料集。

  4. 依序點選「總覽」>「資料表」,然後選取資料表。

  5. 在詳細資料窗格中,依序點選「Edit」(編輯) >「Edit schema」(編輯結構定義)。

  6. 在「Current schema」(目前結構定義) 頁面中,找出要變更的欄位。

  7. 在該欄位的「Mode」(模式) 下拉式選單中,選取「NULLABLE」。

  8. 如要更新設定,請按一下「儲存」。

SQL

使用 ALTER COLUMN DROP NOT NULL DDL 陳述式。以下範例會將 mycolumn 欄的模式從 REQUIRED 變更為 NULLABLE:

  1. 前往 Google Cloud 控制台的「BigQuery」頁面。

    前往「BigQuery」

  2. 在查詢編輯器中輸入下列陳述式:

    ALTER TABLE mydataset.mytable
    ALTER COLUMN mycolumn
    DROP NOT NULL;

  3. 按一下「 執行」。

如要進一步瞭解如何執行查詢,請參閱「執行互動式查詢」。

bq

  1. 首先,請發出 bq show 指令並加上 --schema 旗標,並將現有的資料表結構定義寫入檔案。如果更新的資料表位於預設專案以外的專案,請將專案 ID 新增至資料集名稱,格式如下:PROJECT_ID:DATASET。

    bq show \
    --schema \
    --format=prettyjson \
    PROJECT_ID:DATASET.TABLE > SCHEMA_FILE

    更改下列內容:

    • PROJECT_ID:您的專案 ID。
    • DATASET:資料集名稱,其中包含您要更新的資料表。
    • TABLE:要更新的資料表名稱。
    • SCHEMA_FILE:寫入本機電腦的結構定義定義檔。

    舉例來說,如要將 mydataset.mytable 的結構定義寫入檔案,請輸入下列指令。mydataset.mytable 位於您的預設專案中。

      bq show \
      --schema \
      --format=prettyjson \
      mydataset.mytable > /tmp/myschema.json
    
  2. 在文字編輯器中開啟結構定義檔。結構定義應如下所示:

    [
      {
        "mode": "REQUIRED",
        "name": "column1",
        "type": "STRING"
      },
      {
        "mode": "REQUIRED",
        "name": "column2",
        "type": "FLOAT"
      },
      {
        "mode": "REPEATED",
        "name": "column3",
        "type": "STRING"
      }
    ]
    
  3. 將現有的資料欄模式從 REQUIRED 變更為 NULLABLE。在這個範例中,column1 的模式已放寬。

    [
      {
        "mode": "NULLABLE",
        "name": "column1",
        "type": "STRING"
      },
      {
        "mode": "REQUIRED",
        "name": "column2",
        "type": "FLOAT"
      },
      {
        "mode": "REPEATED",
        "name": "column3",
        "type": "STRING"
      }
    ]
    

    如要深入瞭解如何使用 JSON 結構定義檔案,請參閱指定 JSON 結構定義檔案。

  4. 更新結構定義檔後,請發出下列指令來更新資料表的結構定義。如果您要更新的資料表位於預設專案以外的專案,請按照下列格式將專案 ID 新增至資料集名稱:PROJECT_ID:DATASET。

    bq update PROJECT_ID:DATASET.TABLE SCHEMA

    更改下列內容:

    • PROJECT_ID:您的專案 ID。
    • DATASET:資料集名稱,其中包含您要更新的資料表。
    • TABLE:要更新的資料表名稱。
    • SCHEMA:本機電腦上的 JSON 結構定義檔案路徑。

    例如,輸入以下指令來更新預設專案中 mydataset.mytable 的結構定義。您本機機器上的 JSON 結構定義檔路徑為 /tmp/myschema.json。

      bq update mydataset.mytable /tmp/myschema.json
    

API

呼叫 tables.patch 並使用 schema 屬性,將結構定義中的 REQUIRED 資料欄變更為 NULLABLE。由於 tables.update 方法會取代整個資料表資源,因此建議使用 tables.patch 方法。

Go

在試用這個範例之前,請先按照「使用用戶端程式庫的 BigQuery 快速入門導覽課程」中的 Go 設定操作說明進行操作。詳情請參閱 BigQuery Go API 參考文件。

如要向 BigQuery 進行驗證,請設定應用程式預設憑證。詳情請參閱「設定用戶端程式庫的驗證機制」。

import (
	"context"
	"fmt"

	"cloud.google.com/go/bigquery"
)

// relaxTableAPI demonstrates modifying the schema of a table to remove the requirement that columns allow
// no NULL values.
func relaxTableAPI(projectID, datasetID, tableID string) error {
	// projectID := "my-project-id"
	// datasetID := "mydatasetid"
	// tableID := "mytableid"
	ctx := context.Background()

	client, err := bigquery.NewClient(ctx, projectID)
	if err != nil {
		return fmt.Errorf("bigquery.NewClient: %v", err)
	}
	defer client.Close()

	// Setup: We first create a table with a schema that's restricts NULL values.
	sampleSchema := bigquery.Schema{
		{Name: "full_name", Type: bigquery.StringFieldType, Required: true},
		{Name: "age", Type: bigquery.IntegerFieldType, Required: true},
	}
	original := &bigquery.TableMetadata{
		Schema: sampleSchema,
	}
	if err := client.Dataset(datasetID).Table(tableID).Create(ctx, original); err != nil {
		return err
	}

	tableRef := client.Dataset(datasetID).Table(tableID)
	meta, err := tableRef.Metadata(ctx)
	if err != nil {
		return err
	}
	// Iterate through the schema to set all Required fields to false (nullable).
	var relaxed bigquery.Schema
	for _, v := range meta.Schema {
		v.Required = false
		relaxed = append(relaxed, v)
	}
	newMeta := bigquery.TableMetadataToUpdate{
		Schema: relaxed,
	}
	if _, err := tableRef.Update(ctx, newMeta, meta.ETag); err != nil {
		return err
	}
	return nil
}

Java

在試用這個範例之前,請先按照「使用用戶端程式庫的 BigQuery 快速入門導覽課程」中的 Java 設定操作說明進行操作。詳情請參閱 BigQuery Java API 參考文件。

如要向 BigQuery 進行驗證,請設定應用程式預設憑證。詳情請參閱「設定用戶端程式庫的驗證機制」。

import com.google.cloud.bigquery.BigQuery;
import com.google.cloud.bigquery.BigQueryException;
import com.google.cloud.bigquery.BigQueryOptions;
import com.google.cloud.bigquery.Field;
import com.google.cloud.bigquery.LegacySQLTypeName;
import com.google.cloud.bigquery.Schema;
import com.google.cloud.bigquery.StandardTableDefinition;
import com.google.cloud.bigquery.Table;

public class RelaxColumnMode {

  public static void runRelaxColumnMode() {
    // TODO(developer): Replace these variables before running the sample.
    String datasetName = "MY_DATASET_NAME";
    String tableId = "MY_TABLE_NAME";
    relaxColumnMode(datasetName, tableId);
  }

  public static void relaxColumnMode(String datasetName, String tableId) {
    try {
      // Initialize client that will be used to send requests. This client only needs to be created
      // once, and can be reused for multiple requests.
      BigQuery bigquery = BigQueryOptions.getDefaultInstance().getService();

      Table table = bigquery.getTable(datasetName, tableId);

      // Create new relaxed schema based on the existing table schema
      Schema relaxedSchema =
          Schema.of(
              // The only supported modification you can make to a column's mode is changing it from
              // REQUIRED to NULLABLE
              // Changing a column's mode from REQUIRED to NULLABLE is also called column relaxation
              // INFO: LegacySQLTypeName will be updated to StandardSQLTypeName in release 1.103.0
              Field.newBuilder("word", LegacySQLTypeName.STRING)
                  .setMode(Field.Mode.NULLABLE)
                  .build(),
              Field.newBuilder("word_count", LegacySQLTypeName.STRING)
                  .setMode(Field.Mode.NULLABLE)
                  .build(),
              Field.newBuilder("corpus", LegacySQLTypeName.STRING)
                  .setMode(Field.Mode.NULLABLE)
                  .build(),
              Field.newBuilder("corpus_date", LegacySQLTypeName.STRING)
                  .setMode(Field.Mode.NULLABLE)
                  .build());

      // Update the table with the new schema
      Table updatedTable =
          table.toBuilder().setDefinition(StandardTableDefinition.of(relaxedSchema)).build();
      updatedTable.update();
      System.out.println("Table schema successfully relaxed.");
    } catch (BigQueryException e) {
      System.out.println("Table schema not relaxed \n" + e.toString());
    }
  }
}

Node.js

在試用這個範例之前,請先按照「使用用戶端程式庫的 BigQuery 快速入門導覽課程」中的 Node.js 設定操作說明進行操作。詳情請參閱 BigQuery Node.js API 參考文件。

如要向 BigQuery 進行驗證,請設定應用程式預設憑證。詳情請參閱「設定用戶端程式庫的驗證機制」。

// Import the Google Cloud client library and create a client
const {BigQuery} = require('@google-cloud/bigquery');
const bigquery = new BigQuery();

async function relaxColumn() {
  /**
   * Changes columns from required to nullable.
   * Assumes existing table with the following schema:
   * [{name: 'Name', type: 'STRING', mode: 'REQUIRED'},
   * {name: 'Age', type: 'INTEGER'},
   * {name: 'Weight', type: 'FLOAT'},
   * {name: 'IsMagic', type: 'BOOLEAN'}];
   */

  /**
   * TODO(developer): Uncomment the following lines before running the sample.
   */
  // const datasetId = 'my_dataset';
  // const tableId = 'my_table';

  const newSchema = [
    {name: 'Name', type: 'STRING', mode: 'NULLABLE'},
    {name: 'Age', type: 'INTEGER'},
    {name: 'Weight', type: 'FLOAT'},
    {name: 'IsMagic', type: 'BOOLEAN'},
  ];

  // Retrieve current table metadata
  const table = bigquery.dataset(datasetId).table(tableId);
  const [metadata] = await table.getMetadata();

  // Update schema
  metadata.schema = newSchema;
  const [apiResponse] = await table.setMetadata(metadata);

  console.log(apiResponse.schema.fields);
}

Python

在試用這個範例之前,請先按照「使用用戶端程式庫的 BigQuery 快速入門導覽課程」中的 Python 設定操作說明進行操作。詳情請參閱 BigQuery Python API 參考文件。

如要向 BigQuery 進行驗證,請設定應用程式預設憑證。詳情請參閱「設定用戶端程式庫的驗證機制」。

使用 SchemaField 物件清單覆寫 Table.schema 屬性,並將 mode 屬性設為 'NULLABLE'

from google.cloud import bigquery

client = bigquery.Client()

# TODO(dev): Change table_id to full name of the table you want to create.
table_id = "your-project.your_dataset.your_table"

table = client.get_table(table_id)
new_schema = []
for field in table.schema:
    if field.mode != "REQUIRED":
        new_schema.append(field)
    else:
        # SchemaField properties cannot be edited after initialization.
        # To make changes, construct new SchemaField objects.
        new_field = field.to_api_repr()
        new_field["mode"] = "NULLABLE"
        relaxed_field = bigquery.SchemaField.from_api_repr(new_field)
        new_schema.append(relaxed_field)

table.schema = new_schema
table = client.update_table(table, ["schema"])

print(f"Updated {table_id} schema: {table.schema}.")

透過附加載入工作建立 NULLABLE 欄

在載入工作中將資料附加至資料表時,您可以放寬資料欄的模式。根據檔案類型,選取下列其中一個選項:

  • 從 CSV 和 JSON 檔案附加資料時,請指定 JSON 結構定義檔案,放寬個別資料欄的模式。
  • 從 Avro、ORC 或 Parquet 檔案附加資料時,請將結構定義中的資料欄放寬為 NULL,並讓結構定義推論偵測放寬的資料欄。
。

在載入工作中將資料附加至資料表時,如要將資料欄從 REQUIRED 放寬為 NULLABLE,請選取下列其中一個選項:

控制台

您無法使用 Google Cloud 控制台放寬資料欄模式。

bq

請使用 bq load 指令載入資料並指定 --noreplace 旗標,以指出您要將資料附加至現有的資料表。

如果您要附加的資料是 CSV 或以換行符號分隔的 JSON 格式,請在本機 JSON 結構定義檔案中指定寬鬆資料欄,或使用 --autodetect 旗標,透過結構定義偵測功能找出來源資料中的寬鬆資料欄。

系統可以從 Avro、ORC 和 Parquet 檔案自動推斷放寬的資料欄。不過,資料欄放寬不適用於 Datastore 匯出檔的附加作業。透過載入 Datastore 匯出檔所建立的資料表,其資料欄一律為 NULLABLE。

將 --schema_update_option 旗標設為 ALLOW_FIELD_RELAXATION,以指出您要附加的資料中包含放寬的資料欄。

如果您要附加的資料表位於預設專案以外的資料集,請按照下列格式,將專案 ID 新增至資料集名稱:PROJECT_ID:DATASET。

(選用) 提供 --location 旗標,並將值設為您的位置。

輸入 load 指令,如下所示:

bq --location=LOCATION load \
--noreplace \
--schema_update_option=ALLOW_FIELD_RELAXATION \
--source_format=FORMAT \
PROJECT_ID:DATASET.TABLE \
PATH_TO_SOURCE \
SCHEMA

更改下列內容:

  • LOCATION:位置名稱。--location 是選用旗標。舉例來說,如果您在東京地區使用 BigQuery,就可將該旗標的值設為 asia-northeast1。您可以使用 .bigqueryrc 檔來設定位置的預設值。
  • FORMAT:NEWLINE_DELIMITED_JSON、CSV、PARQUET、ORC 或 AVRO。DATASTORE_BACKUP 檔案不需要放寬資料欄限制。從 Datastore 匯出檔所建立的資料表,其資料欄一律為 NULLABLE。
  • PROJECT_ID:您的專案 ID。
  • DATASET:資料表所在資料集的名稱。
  • TABLE:要附加的資料表名稱。
  • PATH_TO_SOURCE:完整的 Cloud Storage URI、以半形逗號分隔的 URI 清單,或本機電腦上的資料檔案路徑。
  • SCHEMA:本機 JSON 結構定義檔案的路徑。這個選項僅用於 CSV 和 JSON 檔案。系統會從 Avro 檔案自動推斷放寬的資料欄。

範例:

輸入下列指令,使用載入工作將本機 Avro 資料檔案 (/tmp/mydata.avro) 附加至 mydataset.mytable。由於寬鬆資料欄可從 Avro 資料自動推論,因此您不必指定結構定義檔案。mydataset 在您的預設專案中。

bq load \
    --noreplace \
    --schema_update_option=ALLOW_FIELD_RELAXATION \
    --source_format=AVRO \
    mydataset.mytable \
    /tmp/mydata.avro

輸入下列指令,使用載入工作將 Cloud Storage 上採用換行符號分隔格式之 JSON 檔案中的資料附加至 mydataset.mytable。包含放寬資料欄的結構定義位於本機 JSON 結構定義檔 (/tmp/myschema.json) 中。mydataset 位於您的預設專案中。

bq load \
--noreplace \
--schema_update_option=ALLOW_FIELD_RELAXATION \
--source_format=NEWLINE_DELIMITED_JSON \
mydataset.mytable \
gs://mybucket/mydata.json \
/tmp/myschema.json

輸入下列指令,使用載入工作將本機機器上 CSV 檔案中的資料附加至 mydataset.mytable。該指令使用結構定義自動偵測功能來找出來源資料中放寬的資料欄。mydataset 在 myotherproject 中,而不在您的預設專案中。

bq load \
--noreplace \
--schema_update_option=ALLOW_FIELD_RELAXATION \
--source_format=CSV \
--autodetect \
myotherproject:mydataset.mytable \
mydata.csv

API

呼叫 jobs.insert 方法。設定 load 工作和下列屬性:

  • 使用 sourceUris 屬性參照 Cloud Storage 中的資料。
  • 設定 sourceFormat 屬性來指定資料格式。
  • 在 schema 屬性中指定結構定義。
  • 使用 schemaUpdateOptions 屬性指定結構定義更新選項。
  • 使用 writeDisposition 屬性將目的地資料表的寫入處理方式設為 WRITE_APPEND。

Go

在試用這個範例之前,請先按照「使用用戶端程式庫的 BigQuery 快速入門導覽課程」中的 Go 設定操作說明進行操作。詳情請參閱 BigQuery Go API 參考文件。

如要向 BigQuery 進行驗證,請設定應用程式預設憑證。詳情請參閱「設定用戶端程式庫的驗證機制」。

import (
	"context"
	"fmt"
	"os"

	"cloud.google.com/go/bigquery"
)

// relaxTableImport demonstrates amending the schema of a table to relax columns from
// not allowing NULL values to allowing them.
func relaxTableImport(projectID, datasetID, tableID, filename string) error {
	// projectID := "my-project-id"
	// datasetID := "mydataset"
	// tableID := "mytable"
	ctx := context.Background()
	client, err := bigquery.NewClient(ctx, projectID)
	if err != nil {
		return fmt.Errorf("bigquery.NewClient: %v", err)
	}
	defer client.Close()

	sampleSchema := bigquery.Schema{
		{Name: "full_name", Type: bigquery.StringFieldType, Required: true},
		{Name: "age", Type: bigquery.IntegerFieldType, Required: true},
	}
	meta := &bigquery.TableMetadata{
		Schema: sampleSchema,
	}
	tableRef := client.Dataset(datasetID).Table(tableID)
	if err := tableRef.Create(ctx, meta); err != nil {
		return err
	}
	// Now, import data from a local file, but specify relaxation of required
	// fields as a side effect while the data is appended.
	f, err := os.Open(filename)
	if err != nil {
		return err
	}
	source := bigquery.NewReaderSource(f)
	source.AutoDetect = true   // Allow BigQuery to determine schema.
	source.SkipLeadingRows = 1 // CSV has a single header line.

	loader := client.Dataset(datasetID).Table(tableID).LoaderFrom(source)
	loader.SchemaUpdateOptions = []string{"ALLOW_FIELD_RELAXATION"}
	job, err := loader.Run(ctx)
	if err != nil {
		return err
	}
	status, err := job.Wait(ctx)
	if err != nil {
		return err
	}
	if err := status.Err(); err != nil {
		return err
	}
	return nil
}

Java

在試用這個範例之前,請先按照「使用用戶端程式庫的 BigQuery 快速入門導覽課程」中的 Java 設定操作說明進行操作。詳情請參閱 BigQuery Java API 參考文件。

如要向 BigQuery 進行驗證,請設定應用程式預設憑證。詳情請參閱「設定用戶端程式庫的驗證機制」。

import com.google.cloud.bigquery.BigQuery;
import com.google.cloud.bigquery.BigQueryException;
import com.google.cloud.bigquery.BigQueryOptions;
import com.google.cloud.bigquery.CsvOptions;
import com.google.cloud.bigquery.Field;
import com.google.cloud.bigquery.Job;
import com.google.cloud.bigquery.JobInfo;
import com.google.cloud.bigquery.LoadJobConfiguration;
import com.google.cloud.bigquery.Schema;
import com.google.cloud.bigquery.StandardSQLTypeName;
import com.google.cloud.bigquery.Table;
import com.google.cloud.bigquery.TableId;
import com.google.common.collect.ImmutableList;

// Sample to append relax column in a table.
public class RelaxColumnLoadAppend {

  public static void main(String[] args) {
    // TODO(developer): Replace these variables before running the sample.
    String datasetName = "MY_DATASET_NAME";
    String tableName = "MY_TABLE_NAME";
    String sourceUri = "gs://cloud-samples-data/bigquery/us-states/us-states.csv";
    relaxColumnLoadAppend(datasetName, tableName, sourceUri);
  }

  public static void relaxColumnLoadAppend(String datasetName, String tableName, String sourceUri) {
    try {
      // Initialize client that will be used to send requests. This client only needs to be created
      // once, and can be reused for multiple requests.
      BigQuery bigquery = BigQueryOptions.getDefaultInstance().getService();

      // Retrieve destination table reference
      Table table = bigquery.getTable(TableId.of(datasetName, tableName));

      // column as a 'REQUIRED' field.
      Field name =
          Field.newBuilder("name", StandardSQLTypeName.STRING).setMode(Field.Mode.REQUIRED).build();
      Field postAbbr =
          Field.newBuilder("post_abbr", StandardSQLTypeName.STRING)
              .setMode(Field.Mode.REQUIRED)
              .build();
      Schema schema = Schema.of(name, postAbbr);

      // Skip header row in the file.
      CsvOptions csvOptions = CsvOptions.newBuilder().setSkipLeadingRows(1).build();

      // Set job options
      LoadJobConfiguration loadConfig =
          LoadJobConfiguration.newBuilder(table.getTableId(), sourceUri)
              .setSchema(schema)
              .setFormatOptions(csvOptions)
              .setSchemaUpdateOptions(
                  ImmutableList.of(JobInfo.SchemaUpdateOption.ALLOW_FIELD_RELAXATION))
              .setWriteDisposition(JobInfo.WriteDisposition.WRITE_APPEND)
              .build();

      // Create a load job and wait for it to complete.
      Job job = bigquery.create(JobInfo.of(loadConfig));
      job = job.waitFor();
      // Check the job's status for errors
      if (job.isDone() && job.getStatus().getError() == null) {
        System.out.println("Relax column append successfully loaded in a table");
      } else {
        System.out.println(
            "BigQuery was unable to load into the table due to an error:"
                + job.getStatus().getError());
      }
    } catch (BigQueryException | InterruptedException e) {
      System.out.println("Column not added during load append \n" + e.toString());
    }
  }
}

Node.js

在試用這個範例之前,請先按照「使用用戶端程式庫的 BigQuery 快速入門導覽課程」中的 Node.js 設定操作說明進行操作。詳情請參閱 BigQuery Node.js API 參考文件。

如要向 BigQuery 進行驗證,請設定應用程式預設憑證。詳情請參閱「設定用戶端程式庫的驗證機制」。

// Import the Google Cloud client libraries
const {BigQuery} = require('@google-cloud/bigquery');

// Instantiate client
const bigquery = new BigQuery();

async function relaxColumnLoadAppend() {
  // Changes required column to nullable in load append job.

  /**
   * TODO(developer): Uncomment the following lines before running the sample.
   */
  // const fileName = '/path/to/file.csv';
  // const datasetId = 'my_dataset';
  // const tableId = 'my_table';

  // In this example, the existing table contains the 'Name'
  // column as a 'REQUIRED' field.
  const schema = 'Age:INTEGER, Weight:FLOAT, IsMagic:BOOLEAN';

  // Retrieve destination table reference
  const [table] = await bigquery
    .dataset(datasetId)
    .table(tableId)
    .get();
  const destinationTableRef = table.metadata.tableReference;

  // Set load job options
  const options = {
    schema: schema,
    schemaUpdateOptions: ['ALLOW_FIELD_RELAXATION'],
    writeDisposition: 'WRITE_APPEND',
    destinationTable: destinationTableRef,
  };

  // Load data from a local file into the table
  const [job] = await bigquery
    .dataset(datasetId)
    .table(tableId)
    .load(fileName, options);

  console.log(`Job ${job.id} completed.`);

  // Check the job's status for errors
  const errors = job.status.errors;
  if (errors && errors.length > 0) {
    throw errors;
  }
}

Python

在試用這個範例之前,請先按照「使用用戶端程式庫的 BigQuery 快速入門導覽課程」中的 Python 設定操作說明進行操作。詳情請參閱 BigQuery Python API 參考文件。

如要向 BigQuery 進行驗證,請設定應用程式預設憑證。詳情請參閱「設定用戶端程式庫的驗證機制」。

# from google.cloud import bigquery
# client = bigquery.Client()
# project = client.project
# dataset_ref = bigquery.DatasetReference(project, 'my_dataset')
# filepath = 'path/to/your_file.csv'

# Retrieves the destination table and checks the number of required fields
table_id = "my_table"
table_ref = dataset_ref.table(table_id)
table = client.get_table(table_ref)
original_required_fields = sum(field.mode == "REQUIRED" for field in table.schema)
# In this example, the existing table has 3 required fields.
print("{} fields in the schema are required.".format(original_required_fields))

# Configures the load job to append the data to a destination table,
# allowing field relaxation
job_config = bigquery.LoadJobConfig()
job_config.write_disposition = bigquery.WriteDisposition.WRITE_APPEND
job_config.schema_update_options = [
    bigquery.SchemaUpdateOption.ALLOW_FIELD_RELAXATION
]
# In this example, the existing table contains three required fields
# ('full_name', 'age', and 'favorite_color'), while the data to load
# contains only the first two fields.
job_config.schema = [
    bigquery.SchemaField("full_name", "STRING", mode="REQUIRED"),
    bigquery.SchemaField("age", "INTEGER", mode="REQUIRED"),
]
job_config.source_format = bigquery.SourceFormat.CSV
job_config.skip_leading_rows = 1

with open(filepath, "rb") as source_file:
    job = client.load_table_from_file(
        source_file,
        table_ref,
        location="US",  # Must match the destination dataset location.
        job_config=job_config,
    )  # API request

job.result()  # Waits for table load to complete.
print(
    "Loaded {} rows into {}:{}.".format(
        job.output_rows, dataset_id, table_ref.table_id
    )
)

# Checks the updated number of required fields
table = client.get_table(table)
current_required_fields = sum(field.mode == "REQUIRED" for field in table.schema)
print("{} fields in the schema are now required.".format(current_required_fields))

透過附加工作將所有資料欄設為 NULLABLE

將查詢結果附加至資料表時,您可以放寬資料表中的所有資料欄。您可以將 --schema_update_option 旗標設為 ALLOW_FIELD_RELAXATION,放寬目的地資料表中所有必要欄位的限制。您無法使用查詢附加功能,放寬目的地資料表中的個別資料欄。如要透過載入附加工作放寬個別資料欄,請參閱「使用附加工作將資料欄設為 NULLABLE」。

如要在將查詢結果附加至目的地資料表時放寬所有資料欄,請選取下列其中一個選項:

控制台

您無法使用 Google Cloud 控制台放寬資料欄模式。

bq

使用 bq query 指令查詢資料,並指定 --destination_table 旗標來指出您要附加的資料表。

如要指定將查詢結果附加至現有目的地資料表,請指定 --append_table 旗標。

將 --schema_update_option 旗標設為 ALLOW_FIELD_RELAXATION,以指出您要附加之資料表中的所有 REQUIRED 資料欄,都應變更為 NULLABLE。

指定 use_legacy_sql=false 旗標,以便在查詢中使用 GoogleSQL 語法。

如果您要附加的資料表位於預設專案以外的資料集,請按照下列格式,將專案 ID 新增至資料集名稱:PROJECT_ID:DATASET。

(選用) 提供 --location 旗標,並將值設為您的位置。

bq --location=LOCATION query \
--destination_table PROJECT_ID:DATASET.TABLE \
--append_table \
--schema_update_option=ALLOW_FIELD_RELAXATION \
--use_legacy_sql=false \
'QUERY'

更改下列內容:

  • LOCATION:位置名稱。--location 是選用旗標。舉例來說,如果您在東京地區使用 BigQuery,就可將該旗標的值設為 asia-northeast1。您可以使用 .bigqueryrc 檔來設定位置的預設值。
  • PROJECT_ID:您的專案 ID。
  • DATASET:包含要附加資料表的資料集名稱。
  • TABLE:要附加的資料表名稱。
  • QUERY:GoogleSQL 語法的查詢。

範例:

在預設專案中輸入下列指令查詢 mydataset.mytable,將查詢結果附加至 mydataset.mytable2 (同樣位於預設專案)。該指令會將目的地資料表中的所有 REQUIRED 資料欄變更為 NULLABLE。

bq query \
    --destination_table mydataset.mytable2 \
    --append_table \
    --schema_update_option=ALLOW_FIELD_RELAXATION \
    --use_legacy_sql=false \
    'SELECT
       column1,column2
     FROM
       mydataset.mytable'

輸入下列指令,查詢預設專案中的 mydataset.mytable,並將查詢結果附加至位於 myotherproject 中的 mydataset.mytable2。該指令會將目的地資料表中的所有 REQUIRED 資料欄變更為 NULLABLE。

bq query \
--destination_table myotherproject:mydataset.mytable2 \
--append_table \
--schema_update_option=ALLOW_FIELD_RELAXATION \
--use_legacy_sql=false \
'SELECT
   column1,column2
 FROM
   mydataset.mytable'

API

呼叫 jobs.insert 方法。設定 query 工作和下列屬性:

  • 使用 destinationTable 屬性指定目的地資料表。
  • 使用 writeDisposition 屬性將目的地資料表的寫入處理方式設為 WRITE_APPEND。
  • 使用 schemaUpdateOptions 屬性指定結構定義更新選項。
  • 使用 query 屬性指定 GoogleSQL 查詢。

Go

在試用這個範例之前,請先按照「使用用戶端程式庫的 BigQuery 快速入門導覽課程」中的 Go 設定操作說明進行操作。詳情請參閱 BigQuery Go API 參考文件。

如要向 BigQuery 進行驗證,請設定應用程式預設憑證。詳情請參閱「設定用戶端程式庫的驗證機制」。

import (
	"context"
	"fmt"

	"cloud.google.com/go/bigquery"
)

// relaxTableQuery demonstrates relaxing the schema of a table by appending query results to
// enable the table to allow NULL values.
func relaxTableQuery(projectID, datasetID, tableID string) error {
	// projectID := "my-project-id"
	// datasetID := "mydataset"
	// tableID := "mytable"
	ctx := context.Background()
	client, err := bigquery.NewClient(ctx, projectID)
	if err != nil {
		return fmt.Errorf("bigquery.NewClient: %v", err)
	}
	defer client.Close()

	sampleSchema := bigquery.Schema{
		{Name: "full_name", Type: bigquery.StringFieldType, Required: true},
		{Name: "age", Type: bigquery.IntegerFieldType, Required: true},
	}
	meta := &bigquery.TableMetadata{
		Schema: sampleSchema,
	}
	tableRef := client.Dataset(datasetID).Table(tableID)
	if err := tableRef.Create(ctx, meta); err != nil {
		return err
	}
	// Now, append a query result that includes nulls, but allow the job to relax
	// all required columns.
	q := client.Query("SELECT \"Beyonce\" as full_name")
	q.QueryConfig.Dst = client.Dataset(datasetID).Table(tableID)
	q.SchemaUpdateOptions = []string{"ALLOW_FIELD_RELAXATION"}
	q.WriteDisposition = bigquery.WriteAppend
	q.Location = "US"
	job, err := q.Run(ctx)
	if err != nil {
		return err
	}
	_, err = job.Wait(ctx)
	if err != nil {
		return err
	}
	return nil
}

Java

在試用這個範例之前,請先按照「使用用戶端程式庫的 BigQuery 快速入門導覽課程」中的 Java 設定操作說明進行操作。詳情請參閱 BigQuery Java API 參考文件。

如要向 BigQuery 進行驗證,請設定應用程式預設憑證。詳情請參閱「設定用戶端程式庫的驗證機制」。

import com.google.cloud.bigquery.BigQuery;
import com.google.cloud.bigquery.BigQueryException;
import com.google.cloud.bigquery.BigQueryOptions;
import com.google.cloud.bigquery.Job;
import com.google.cloud.bigquery.JobInfo;
import com.google.cloud.bigquery.JobInfo.SchemaUpdateOption;
import com.google.cloud.bigquery.JobInfo.WriteDisposition;
import com.google.cloud.bigquery.QueryJobConfiguration;
import com.google.cloud.bigquery.TableId;
import com.google.cloud.bigquery.TableResult;
import com.google.common.collect.ImmutableList;

public class RelaxTableQuery {

  public static void runRelaxTableQuery() throws Exception {
    // TODO(developer): Replace these variables before running the sample.
    String projectId = "MY_PROJECT_ID";
    String datasetName = "MY_DATASET_NAME";
    String tableName = "MY_TABLE_NAME";
    relaxTableQuery(projectId, datasetName, tableName);
  }

  // To relax all columns in a destination table when you append data to it during a query job
  public static void relaxTableQuery(String projectId, String datasetName, String tableName)
      throws Exception {
    try {
      // Initialize client that will be used to send requests. This client only needs to be created
      // once, and can be reused for multiple requests.
      BigQuery bigquery = BigQueryOptions.getDefaultInstance().getService();

      TableId tableId = TableId.of(datasetName, tableName);

      String sourceTable = "`" + projectId + "." + datasetName + "." + tableName + "`";
      String query = "SELECT word FROM " + sourceTable + " WHERE word like '%is%'";

      QueryJobConfiguration queryConfig =
          QueryJobConfiguration.newBuilder(query)
              // Use standard SQL syntax for queries.
              // See: https://cloud.google.com/bigquery/sql-reference/
              .setUseLegacySql(false)
              .setSchemaUpdateOptions(ImmutableList.of(SchemaUpdateOption.ALLOW_FIELD_RELAXATION))
              .setWriteDisposition(WriteDisposition.WRITE_APPEND)
              .setDestinationTable(tableId)
              .build();

      Job queryJob = bigquery.create(JobInfo.newBuilder(queryConfig).build());

      queryJob = queryJob.waitFor();

      // Check for errors
      if (queryJob == null) {
        throw new Exception("Job no longer exists");
      } else if (queryJob.getStatus().getError() != null) {
        // You can also look at queryJob.getStatus().getExecutionErrors() for all
        // errors, not just the latest one.
        throw new Exception(queryJob.getStatus().getError().toString());
      }

      // Get the results.
      TableResult results = queryJob.getQueryResults();

      // Print all pages of the results.
      results
          .iterateAll()
          .forEach(
              rows -> {
                rows.forEach(row -> System.out.println("row: " + row.toString()));
              });

      System.out.println("Successfully relaxed all columns in destination table during query job");
    } catch (BigQueryException | InterruptedException e) {
      System.out.println("Columns not relaxed during query job \n" + e.toString());
    }
  }
}

Python

在試用這個範例之前,請先按照「使用用戶端程式庫的 BigQuery 快速入門導覽課程」中的 Python 設定操作說明進行操作。詳情請參閱 BigQuery Python API 參考文件。

如要向 BigQuery 進行驗證,請設定應用程式預設憑證。詳情請參閱「設定用戶端程式庫的驗證機制」。

from google.cloud import bigquery

# Construct a BigQuery client object.
client = bigquery.Client()

# TODO(developer): Set table_id to the ID of the destination table.
# table_id = "your-project.your_dataset.your_table_name"

# Retrieves the destination table and checks the number of required fields.
table = client.get_table(table_id)  # Make an API request.
original_required_fields = sum(field.mode == "REQUIRED" for field in table.schema)

# In this example, the existing table has 2 required fields.
print("{} fields in the schema are required.".format(original_required_fields))

# Configures the query to append the results to a destination table,
# allowing field relaxation.
job_config = bigquery.QueryJobConfig(
    destination=table_id,
    schema_update_options=[bigquery.SchemaUpdateOption.ALLOW_FIELD_RELAXATION],
    write_disposition=bigquery.WriteDisposition.WRITE_APPEND,
)

# Start the query, passing in the extra configuration.
client.query_and_wait(
    # In this example, the existing table contains 'full_name' and 'age' as
    # required columns, but the query results will omit the second column.
    'SELECT "Beyonce" as full_name;',
    job_config=job_config,
)  # Make an API request and wait for job to complete

# Checks the updated number of required fields.
table = client.get_table(table_id)  # Make an API request.
current_required_fields = sum(field.mode == "REQUIRED" for field in table.schema)
print("{} fields in the schema are now required.".format(current_required_fields))

變更資料欄的預設值

如要變更資料欄的預設值,請選取下列其中一個選項:

控制台

  1. 前往 Google Cloud 控制台的「BigQuery」頁面。

    前往「BigQuery」

  2. 點選左側窗格中的 「Explorer」。

    醒目顯示的「Explorer」窗格按鈕。

    如果沒有看到左側窗格,請按一下 「Expand left pane」(展開左側窗格),開啟窗格。

  3. 在「Explorer」窗格中展開專案,點選「Datasets」(資料集),然後選取資料集。

  4. 依序點選「總覽」>「資料表」,然後點選所需資料表。

  5. 在詳細資料窗格中,依序點選「Edit」(編輯) >「Edit schema」(編輯結構定義)。

  6. 在「Current schema」(目前結構定義) 頁面中,找出要變更的頂層欄位。

  7. 輸入該欄位的預設值。

  8. 按一下「儲存」。

SQL

使用 ALTER COLUMN SET DEFAULT DDL 陳述式。

  1. 前往 Google Cloud 控制台的「BigQuery」頁面。

    前往「BigQuery」

  2. 在查詢編輯器中輸入下列陳述式:

    ALTER TABLE mydataset.mytable
    ALTER COLUMN column_name SET DEFAULT default_expression;

  3. 按一下「 執行」。

如要進一步瞭解如何執行查詢,請參閱「執行互動式查詢」。

變更資料欄說明

如要變更資料欄說明,請選取下列其中一個選項:

控制台

  1. 前往 Google Cloud 控制台的「BigQuery」頁面。

    前往「BigQuery」

  2. 點選左側窗格中的 「Explorer」。

    醒目顯示的「Explorer」窗格按鈕。

  3. 在「Explorer」窗格中展開專案,點選「Datasets」(資料集),然後選取資料集。

  4. 依序點選「總覽」>「資料表」,然後選取資料表。

  5. 在詳細資料窗格中,依序點選「Edit」(編輯) >「Edit schema」(編輯結構定義)。

  6. 在「Current schema」(目前結構定義) 頁面中,找出要變更的欄位。

  7. 輸入該欄位的說明。

  8. 按一下「儲存」。

SQL

使用 ALTER COLUMN SET OPTIONS DDL 陳述式。

  1. 前往 Google Cloud 控制台的「BigQuery」頁面。

    前往「BigQuery」

  2. 在查詢編輯器中輸入下列陳述式:

    ALTER TABLE mydataset.mytable
    ALTER COLUMN column_name
    SET OPTIONS (description = 'This is a column description.');

  3. 按一下「 執行」。

如要進一步瞭解如何執行查詢,請參閱「執行互動式查詢」。

Gemini

您可以使用 Gemini in BigQuery,透過資料洞察生成資料欄說明。資料洞察功能可自動探索、瞭解及整理資料。

如要進一步瞭解資料洞察,包括設定步驟、必要的 IAM 角色,以及提高生成的洞察資料準確率的最佳做法,請參閱「在 BigQuery 生成資料洞察」。

  1. 前往 Google Cloud 控制台的「BigQuery」頁面。

    前往「BigQuery」

  2. 點選左側窗格中的 「Explorer」。

    醒目顯示的「Explorer」窗格按鈕。

  3. 在「Explorer」窗格中展開專案和資料集,然後選取資料表。

  4. 在詳細資料窗格中,按一下「Details」(詳細資料) 分頁標籤。

  5. 按一下「資料表資訊」旁的「描述資料」。

    Gemini 會生成資料欄說明和資料表洞察。系統需要幾分鐘才能填入資訊。您可以在資料表的「Insights」(洞察) 分頁查看生成的洞察資料。

  6. 如要編輯並儲存生成的資料欄說明,請按照下列步驟操作:

    1. 按一下「表格資訊」旁邊的「查看說明」。

    2. 在「資料欄說明」部分,按一下「儲存至結構定義」。

      生成的資料欄說明會填入各欄的「New description」(新說明) 欄位。

    3. 視需要編輯資料欄說明,然後按一下「儲存」。

      資料欄說明會立即更新。

    4. 如要關閉「Preview descriptions」(預覽說明) 面板,請按一下 「Close」(關閉)。

刪除資料欄

您可以使用 ALTER TABLE DROP COLUMN DDL 陳述式,從現有資料表刪除資料欄。

陳述式不會立即釋出與捨棄資料欄相關聯的儲存空間。如要進一步瞭解刪除儲存空間中的資料欄對儲存空間的影響,請參閱 ALTER TABLE DROP COLUMN 陳述式詳細資料。您可以透過以下兩種方式立即釋出儲存空間:

  • 使用 SELECT * EXCEPT 查詢覆寫資料表:

    CREATE OR REPLACE TABLE mydataset.mytable AS (
      SELECT * EXCEPT (column_to_delete) FROM mydataset.mytable
    );
    
  • 將資料匯出至 Cloud Storage、刪除不必要的資料欄,然後將資料載入至結構定義正確的新資料表。

結構定義更新無法捨棄巢狀欄位。