테이블 스키마 수정

이 문서에서는 기존 BigQuery 테이블의 스키마 정의를 수정하는 방법을 설명합니다. BigQuery는 기본적으로 다음과 같은 스키마 수정 작업을 지원합니다.

  • 스키마 정의에 열 추가
  • 열 모드를 REQUIRED에서 NULLABLE로 완화

초기 스키마를 정의하지 않고 테이블을 만든 다음 나중에 테이블에 스키마 정의를 추가할 수 있습니다.

다음과 같은 다른 모든 스키마 수정 작업은 지원되지 않으므로 수동으로 해결해야 합니다.

  • 열 이름 변경
  • 열의 데이터 유형 변경
  • 열 모드 변경(REQUIRED열을 NULLABLE로 완화하는 작업 제외)
  • 열 삭제

별도의 해결 방법이 필요한 지원되지 않는 스키마 변경 작업에 대한 자세한 내용은 테이블 스키마 수동 변경을 참조하세요.

테이블의 스키마 정의에 열 추가

다음과 같은 방법으로 기존 테이블의 스키마 정의에 열을 추가할 수 있습니다.

  • 수동으로(빈 열 만들기)
  • 로드 또는 쿼리 작업을 사용하여 테이블을 덮어쓸 때
  • 로드 또는 쿼리 작업을 사용하여 테이블에 데이터를 추가할 때

추가하는 모든 열은 BigQuery의 열 이름 규칙을 준수해야 합니다. 스키마 구성요소를 만드는 자세한 방법은 스키마 지정을 참조하세요.

수동으로 빈 열 추가

기존 테이블에 빈 열을 추가하는 방법은 다음과 같습니다.

  • Cloud Console 또는 기본 BigQuery 웹 UI 사용
  • bq 명령줄 도구의 bq update 명령어 사용
  • tables.patch API 메서드 호출
  • 클라이언트 라이브러리 사용

기존 테이블 스키마에 새 열을 추가하는 경우 열은 NULLABLE 또는 REPEATED여야 합니다. 기존 테이블 스키마에는 REQUIRED 열을 추가할 수 없습니다. API 또는 bq 명령줄 도구에서 기존 테이블 스키마에 REQUIRED 열을 추가하려고 하면 다음 오류가 반환됩니다. BigQuery error in update operation: Provided Schema does not match Table project_id:dataset.table. Cannot add required columns to an existing schema. REQUIRED 열은 테이블을 만들면서 데이터를 로드하거나 스키마 정의가 없는 빈 테이블을 만들 때만 추가할 수 있습니다.

테이블의 스키마 정의에 새 열을 추가한 후에는 다음과 같은 방법으로 새 열에 데이터를 로드할 수 있습니다.

테이블의 스키마 정의에 빈 열을 추가하려면 다음 안내를 따르세요.

Console

  1. 리소스 창에서 테이블을 선택합니다.

  2. 쿼리 편집기 아래에서 스키마 섹션 하단으로 스크롤하고 스키마 수정을 클릭합니다.

테이블 스키마 수정

  1. 열리는 패널의 하단으로 스크롤하고 필드 추가를 클릭합니다.

    • 이름에 열 이름을 입력합니다.
    • 유형에서 데이터 유형을 선택합니다.
    • 모드에서 NULLABLE 또는 REPEATED를 선택합니다.
  2. 열 추가가 끝나면 저장을 클릭합니다.

기본 UI

  1. 탐색 창에서 테이블을 선택합니다.

  2. 테이블 세부정보 페이지에서 새 필드 추가를 클릭합니다.

  3. 새 필드 섹션에서 다음 안내를 따릅니다.

    • 이름에 열 이름을 입력합니다.
    • 유형에서 데이터 유형을 선택합니다.
    • 모드에서 NULLABLE 또는 REPEATED를 선택합니다.

      테이블 스키마 업데이트

  4. 열 추가가 끝나면 테이블에 추가를 클릭합니다.

bq

bq update 명령어를 실행하고 JSON 스키마 파일을 제공합니다. 업데이트할 테이블이 기본 프로젝트가 아닌 다른 프로젝트에 있으면 프로젝트 ID를 project_id:dataset 형식으로 데이터 세트 이름에 추가합니다.

bq update project_id:dataset.table schema

여기에서

  • project_id는 프로젝트 ID입니다.
  • dataset는 업데이트할 테이블이 포함되어 있는 데이터 세트의 이름입니다.
  • table은 업데이트할 테이블의 이름입니다.
  • schema는 로컬 머신에 있는 JSON 스키마 파일의 경로입니다.

bq 명령줄 도구를 사용하여 스키마를 지정할 때는 RECORD(STRUCT) 유형을 포함하거나, 열 설명을 포함하거나, 열 모드를 지정할 수 없습니다. 모든 모드는 기본적으로 NULLABLE로 설정됩니다.

인라인 스키마 정의를 사용하여 열을 추가하려면 새 열을 포함하여 전체 스키마 정의를 제공해야 합니다. 인라인 스키마 정의를 사용하여 열 모드를 지정할 수 없기 때문에 업데이트 시 기존 REQUIRED 열을 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. 먼저, --schema 플래그를 지정하여 bq show 명령어를 실행하고 기존 테이블 스키마를 파일에 씁니다. 업데이트할 테이블이 기본 프로젝트가 아닌 다른 프로젝트에 있으면 프로젝트 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. 스키마 정의의 끝에 새 열을 추가합니다. 배열의 다른 위치에 새 열을 추가하려고 하면 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는 로컬 머신에 있는 JSON 스키마 파일의 경로입니다.

    예를 들어 기본 프로젝트에 있는 mydataset.mytable의 스키마 정의를 업데이트하려면 다음 명령어를 입력합니다. 로컬 머신에 있는 스키마 파일의 경로는 /tmp/myschema.json입니다.

    bq update mydataset.mytable /tmp/myschema.json
    

API

tables.patch 메서드를 호출하고 schema 속성을 사용하여 스키마 정의에 빈 열을 추가합니다. tables.update 메서드는 전체 테이블 리소스를 바꾸기 때문에 tables.patch 메서드를 사용하는 것이 좋습니다.

Go

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
}

자바

이 샘플을 사용해 보기 전에 BigQuery 빠른 시작: 클라이언트 라이브러리 사용의 자바 설정 안내를 따르세요. 자세한 내용은 BigQuery 자바 API 참조 문서를 확인하세요.

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 참조 문서를 확인하세요.


// 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 참조 문서를 확인하세요.

Table.schema 사본에 새 SchemaField 객체를 추가한 다음 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에 새 중첩 열을 추가할 수도 있습니다. 새 중첩 열을 추가하는 프로세스는 새 열을 추가하는 프로세스와 매우 유사합니다.

Console

기존 RECORD 열에 새 중첩 필드를 추가하는 기능은 현재 Cloud Console에서 지원되지 않습니다.

기본 UI

기존 RECORD 열에 새 중첩 필드를 추가하는 기능은 현재 기본 BigQuery 웹 UI에서 지원되지 않습니다.

bq

bq update 명령어를 실행하고 기존 RECORD 열의 스키마 정의에 중첩 필드를 추가하는 JSON 스키마 파일을 제공합니다. 업데이트할 테이블이 기본 프로젝트가 아닌 다른 프로젝트에 있으면 프로젝트 ID를 project_id:dataset 형식으로 데이터 세트 이름에 추가합니다.

bq update project_id:dataset.table schema

여기에서

  • project_id는 프로젝트 ID입니다.
  • dataset는 업데이트할 테이블이 포함되어 있는 데이터 세트의 이름입니다.
  • table은 업데이트할 테이블의 이름입니다.
  • schema는 로컬 머신에 있는 JSON 스키마 파일의 경로입니다.

bq 명령줄 도구를 사용하여 스키마를 지정할 때는 RECORD(STRUCT) 유형을 포함하거나, 열 설명을 포함하거나, 열 모드를 지정할 수 없습니다. 모든 모드는 기본적으로 NULLABLE로 설정됩니다. 따라서 RECORD에 새로운 중첩 열을 추가하는 경우 JSON 스키마 파일을 제공해야 합니다.

JSON 스키마 파일을 사용하여 RECORD에 중첩 열을 추가하려면 다음 안내에 따르세요.

  1. 먼저, --schema 플래그를 지정하여 bq show 명령어를 실행하고 기존 테이블 스키마를 파일에 씁니다. 업데이트할 테이블이 기본 프로젝트가 아닌 다른 프로젝트에 있으면 프로젝트 ID를 project_id:dataset.table 형식으로 데이터 세트 이름에 추가합니다.

    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. 텍스트 편집기에서 스키마 파일을 엽니다. 스키마가 다음과 같이 나타납니다. 이 예시에서 column3은 중첩 반복 열입니다. 중첩 열은 nested1nested2입니다. 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의 스키마 정의를 업데이트하려면 다음 명령어를 입력합니다. 로컬 머신에 있는 스키마 파일의 경로는 /tmp/myschema.json입니다.

    bq update mydataset.mytable /tmp/myschema.json
    

API

tables.patch메서드를 호출하고 schema 속성을 사용하여 스키마 정의에 중첩 열을 추가합니다. tables.update 메서드는 전체 테이블 리소스를 바꾸기 때문에 tables.patch 메서드를 사용하는 것이 좋습니다.

데이터를 덮어쓰거나 추가할 때 열 추가

기존 테이블에 데이터를 로드할 때 해당 테이블에 새 열을 추가하고 기존 테이블을 덮어쓸 수 있습니다. 기존 테이블을 덮어쓸 경우 로드하는 데이터의 스키마를 사용하여 기존 테이블의 스키마를 덮어쓰게 됩니다. 로드 작업을 사용하여 테이블을 덮어쓰는 방법은 다음 항목을 참조하세요.

로드 또는 쿼리 작업을 사용하여 기존 테이블에 데이터를 추가하면서 해당 테이블에 새 열을 추가할 수도 있습니다.

로드 추가 작업에서 열 추가

로드 작업에서 데이터를 추가할 때 테이블에 열을 추가하는 방법은 다음과 같습니다.

  • bq 명령줄 도구의 bq load 명령어 사용
  • API의 jobs.insert 메서드를 호출하고 load 작업 구성
  • 클라이언트 라이브러리 사용

현재 Cloud Console 또는 기본 BigQuery 웹 UI에서는 추가 작업 중에 기존 테이블에 열을 추가할 수 없습니다.

로드 작업에서 추가 작업을 사용하여 열을 추가할 경우 업데이트된 스키마는 다음과 같이 지정됩니다.

  • 자동으로 감지됨(CSV 및 JSON 파일의 경우)
  • JSON 스키마 파일에서 지정됨(CSV 및 JSON 파일의 경우)
  • Avro, ORC, Parquet, Datastore 내보내기 파일의 자체 설명적 소스 데이터에서 검색됨

JSON 파일의 스키마를 지정할 경우 새 열은 JSON 파일에서 정의해야 합니다. 새 열 정의가 없으면 데이터를 추가할 때 Error while reading data, error message: parsing error in row starting at position int: No such field: field.라는 오류가 반환됩니다.

추가 작업 중에 새 열을 추가할 경우 기존 행에 대한 새 열의 값은 NULL로 설정됩니다.

로드 작업 중에 테이블에 데이터를 추가하면서 새 열을 추가하려면 다음 안내를 따르세요.

Console

Cloud Console을 사용하여 데이터를 로드할 때는 기존 테이블에 새 열을 추가할 수 없습니다.

기본 UI

기본 BigQuery 웹 UI를 사용하여 데이터를 로드할 때는 기존 테이블에 새 열을 추가할 수 없습니다.

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 파일을 사용하여 위치 기본값을 설정할 수 있습니다.
  • formatNEWLINE_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.avromydataset.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

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
}

자바

이 샘플을 사용해 보기 전에 BigQuery 빠른 시작: 클라이언트 라이브러리 사용의 자바 설정 안내를 따르세요. 자세한 내용은 BigQuery 자바 API 참조 문서를 확인하세요.

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 참조 문서를 확인하세요.

// 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 참조 문서를 확인하세요.

# 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 명령어 사용
  • API의 jobs.insert 메서드를 호출하고 query 작업 구성
  • 클라이언트 라이브러리 사용

현재 Cloud Console 또는 기본 BigQuery 웹 UI에서는 추가 작업 중에 열을 추가할 수 없습니다.

쿼리 작업에서 추가 작업을 사용하여 열을 추가할 경우 대상 테이블의 스키마를 업데이트하는 데는 쿼리 결과의 스키마가 사용됩니다. 한 위치에서 테이블을 쿼리하고 다른 위치의 다른 테이블에 결과를 쓸 수는 없습니다.

쿼리 작업 중에 테이블에 데이터를 추가하면서 새 열을 추가하려면 다음과 같이 하세요.

Console

Cloud Console을 사용하여 쿼리 결과를 추가할 때 기존 테이블에 새 열을 추가할 수 없습니다.

기본 UI

기본 BigQuery 웹 UI를 사용하여 쿼리 결과를 추가할 때는 기존 테이블에 새 열을 추가할 수 없습니다.

bq

bq query 명령어를 사용하여 데이터를 쿼리하고 --destination_table 플래그를 지정하여 데이터를 추가할 테이블을 나타냅니다.

기존 대상 테이블에 쿼리 결과를 추가하도록 지정하려면 --append_table 플래그를 지정합니다.

추가하는 쿼리 결과에 새 열이 포함되어 있음을 나타내려면 --schema_update_option플래그를 ALLOW_FIELD_ADDITION으로 설정해야 합니다.

use_legacy_sql=false 플래그를 지정하여 쿼리에 표준 SQL 구문을 사용합니다.

추가할 테이블이 기본 프로젝트가 아닌 다른 프로젝트의 데이터 세트에 있으면 프로젝트 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는 표준 SQL 구문의 쿼리입니다.

예:

기본 프로젝트에 있는 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을 쿼리하고 쿼리 결과를 myotherprojectmydataset.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 속성을 사용하여 표준 SQL 쿼리를 지정합니다.

Go

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
}

자바

이 샘플을 사용해 보기 전에 BigQuery 빠른 시작: 클라이언트 라이브러리 사용의 자바 설정 안내를 따르세요. 자세한 내용은 BigQuery 자바 API 참조 문서를 확인하세요.

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 참조 문서를 확인하세요.

// 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 참조 문서를 확인하세요.

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.
query_job = client.query(
    # 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.
query_job.result()  # Wait for the 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)))

열 모드 완화

현재 열 모드에 대해 지원되는 수정 작업은 모드를 REQUIRED에서 NULLABLE로 변경하는 것뿐입니다. 열 모드를 REQUIRED에서 NULLABLE로 변경하는 것을 열 완화라고도 합니다. 다음과 같은 방법으로 REQUIRED 열을 완화할 수 있습니다.

  • 수동
  • 로드 또는 쿼리 작업을 사용하여 테이블을 덮어쓸 때
  • 쿼리 작업을 사용하여 테이블에 데이터를 추가할 때

수동으로 REQUIRED 열을 NULLABLE로 변경

열 모드를 REQUIRED에서 NULLABLE로 수동으로 변경하는 방법은 다음과 같습니다.

  • Cloud Console 또는 기본 BigQuery 웹 UI 사용
  • bq 명령줄 도구의 bq update 명령어 사용
  • tables.patch API 메서드를 호출합니다.
  • 클라이언트 라이브러리 사용

열 모드를 REQUIRED에서 NULLABLE로 수동으로 변경하려면 다음과 같이 하세요.

Console

현재는 Cloud Console을 사용하여 열 모드를 완화할 수 없습니다.

기본 UI

  1. 데이터 세트를 펼치고 테이블을 선택합니다.

  2. 테이블 세부정보 페이지에서 스키마 탭을 클릭합니다.

  3. 필수 열 오른쪽의 아래쪽 화살표를 클릭하고 다음 중 하나를 선택합니다.

    • NULLABLE로 설정 — 개별 열의 모드를 완화합니다.
    • REQUIRED를 모두 NULLABLE로 설정 - 스키마 정의의 모든 REQUIRED 열을 NULLABLE로 변경합니다.

      열 모드 완화

  4. 모드 변경 확인 대화상자에서 확인을 클릭하여 모드를 NULLABLE로 변경합니다. 이 변경 작업은 취소할 수 없습니다.

bq

  1. 먼저, --schema 플래그를 지정하여 bq show 명령어를 실행하고 기존 테이블 스키마를 파일에 씁니다. 업데이트할 테이블이 기본 프로젝트가 아닌 다른 프로젝트에 있으면 프로젝트 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의 스키마 정의를 업데이트하려면 다음 명령어를 입력합니다. 로컬 머신에 있는 스키마 파일의 경로는 /tmp/myschema.json입니다.

      bq update mydataset.mytable /tmp/myschema.json
    

API

tables.patch를 호출하고 schema 속성을 사용하여 스키마 정의의 REQUIRED 열을 NULLABLE로 변경합니다. tables.update 메서드는 전체 테이블 리소스를 바꾸기 때문에 tables.patch 메서드를 사용하는 것이 좋습니다.

Go

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
}

자바

이 샘플을 사용해 보기 전에 BigQuery 빠른 시작: 클라이언트 라이브러리 사용의 자바 설정 안내를 따르세요. 자세한 내용은 BigQuery 자바 API 참조 문서를 확인하세요.

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 참조 문서를 확인하세요.

// 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 참조 문서를 확인하세요.

Table.schema 속성을 mode 속성이 'NULLABLE'로 설정된 SchemaField 객체의 목록으로 덮어씁니다.

# from google.cloud import bigquery
# client = bigquery.Client()
# dataset_id = 'my_dataset'
# table_id = 'my_table'

original_schema = [
    bigquery.SchemaField("full_name", "STRING", mode="REQUIRED"),
    bigquery.SchemaField("age", "INTEGER", mode="REQUIRED"),
]

dataset_ref = bigquery.DatasetReference(project, dataset_id)
table_ref = dataset_ref.table(table_id)
table = bigquery.Table(table_ref, schema=original_schema)
table = client.create_table(table)
assert all(field.mode == "REQUIRED" for field in table.schema)

# SchemaField properties cannot be edited after initialization.
# To make changes, construct new SchemaField objects.
relaxed_schema = [
    bigquery.SchemaField("full_name", "STRING", mode="NULLABLE"),
    bigquery.SchemaField("age", "INTEGER", mode="NULLABLE"),
]
table.schema = relaxed_schema
table = client.update_table(table, ["schema"])

assert all(field.mode == "NULLABLE" for field in table.schema)

로드 또는 쿼리 작업에서 REQUIREDNULLABLE로 변경

기존 테이블에 데이터를 로드하고 덮어쓸 경우 기존 테이블의 스키마에서 REQUIRED 열을 NULLABLE로 완화할 수 있습니다. 기존 테이블을 덮어쓸 경우 로드하는 데이터의 스키마를 사용하여 기존 테이블의 스키마를 덮어쓰게 됩니다. 로드 작업을 사용하여 테이블을 덮어쓰는 방법은 다음 항목을 참조하세요.

쿼리 작업을 사용하여 기존 테이블에 데이터를 추가할 때도 REQUIRED 열을 NULLABLE로 완화할 수 있습니다.

로드 추가 작업에서 REQUIREDNULLABLE로 변경

로드 작업에서 데이터를 테이블에 추가할 때 열 모드를 완화하는 방법은 다음과 같습니다.

  • bq 명령줄 도구의 bq load 명령어 사용
  • API의 jobs.insert 메서드를 호출하고 load 작업 구성
  • 클라이언트 라이브러리 사용

현재 Cloud Console 또는 기본 BigQuery 웹 UI에서는 추가 작업 중에 열 모드를 변경할 수 없습니다.

로드 작업에서 추가 작업을 사용하여 열 모드를 완화할 경우 다음을 수행할 수 있습니다.

  • JSON 스키마 파일을 지정하여 개별 열의 모드를 완화합니다(CSV 및 JSON 파일에서 데이터를 추가할 경우)
  • Avro, ORC, Parquet 스키마에서 열을 null로 완화하고 스키마 유추를 통해 완화된 열을 감지할 수 있도록 합니다.

로드 작업 중에 테이블에 데이터를 추가하면서 열을 REQUIRED에서 NULLABLE로 완화하려면 다음과 같이 하세요.

Console

현재는 Cloud Console을 사용하여 열 모드를 완화할 수 없습니다.

기본 UI

기본 BigQuery 웹 UI를 사용하여 로드 작업에서 테이블에 데이터를 추가하는 경우에는 기존 열 모드를 완화할 수 없습니다.

bq

bq load 명령어를 사용하여 데이터를 로드하고 --noreplace 플래그를 지정하여 데이터를 기존 테이블에 추가하도록 합니다.

추가하는 데이터가 CSV 또는 줄바꿈으로 구분된 JSON 형식인 경우에는 로컬 JSON 스키마 파일에서 완화된 열을 지정하거나, 스키마 감지를 통해 소스 데이터의 완화된 열을 감지하도록 --autodetect 플래그를 사용합니다. JSON 스키마 파일을 사용하여 열 모드를 완화하는 방법은 REQUIRED 열을 NULLABLE로 수동 변경을 참조하세요.

완화된 열은 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 파일을 사용하여 위치 기본값을 설정할 수 있습니다.
  • formatNEWLINE_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.avromydataset.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

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
}

자바

이 샘플을 사용해 보기 전에 BigQuery 빠른 시작: 클라이언트 라이브러리 사용의 자바 설정 안내를 따르세요. 자세한 내용은 BigQuery 자바 API 참조 문서를 확인하세요.

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 참조 문서를 확인하세요.

// 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 참조 문서를 확인하세요.

# 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))

쿼리 추가 작업에서 REQUIREDNULLABLE로 변경

쿼리 결과를 추가할 때 테이블에서 모든 열을 완화하는 방법은 다음과 같습니다.

  • bq 명령줄 도구의 bq query 명령어 사용
  • API의 jobs.insert 메서드를 호출하고 query 작업 구성
  • 클라이언트 라이브러리 사용

현재 Cloud Console 또는 기본 BigQuery 웹 UI에서는 추가 작업 중에 열을 완화할 수 없습니다.

쿼리 작업에서 추가 작업을 사용하여 열을 완화하려면 --schema_update_option플래그를 ALLOW_FIELD_RELAXATION으로 설정하여 대상 테이블의 모든 필수 필드를 완화하면 됩니다. 쿼리 추가 작업을 사용하여 대상 테이블의 개별 열을 완화할 수는 없습니다.

쿼리 작업 중에 테이블에 데이터를 추가하면서 대상 테이블의 모든 열을 완화하려면 다음 안내를 따르세요.

Console

현재는 Cloud Console을 사용하여 열 모드를 완화할 수 없습니다.

기본 UI

BigQuery 웹 UI를 사용하여 쿼리 결과를 추가할 때는 대상 테이블의 열을 완화할 수 없습니다.

bq

bq query 명령어를 사용하여 데이터를 쿼리하고 --destination_table 플래그를 지정하여 데이터를 추가할 테이블을 나타냅니다.

기존 대상 테이블에 쿼리 결과를 추가하도록 지정하려면 --append_table 플래그를 지정합니다.

데이터를 추가할 테이블의 모든 REQUIRED 열을 NULLABLE로 변경하도록 지정하려면 --schema_update_option 플래그를 ALLOW_FIELD_RELAXATION으로 설정합니다.

use_legacy_sql=false 플래그를 지정하여 쿼리에 표준 SQL 구문을 사용합니다.

추가할 테이블이 기본 프로젝트가 아닌 다른 프로젝트의 데이터 세트에 있으면 프로젝트 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는 표준 SQL 구문의 쿼리입니다.

예:

기본 프로젝트에 있는 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을 쿼리하고 쿼리 결과를 myotherprojectmydataset.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 속성을 사용하여 표준 SQL 쿼리를 지정합니다.

Go

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
}

자바

이 샘플을 사용해 보기 전에 BigQuery 빠른 시작: 클라이언트 라이브러리 사용의 자바 설정 안내를 따르세요. 자세한 내용은 BigQuery 자바 API 참조 문서를 확인하세요.

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 참조 문서를 확인하세요.

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.
query_job = client.query(
    # 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.
query_job.result()  # Wait for the 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))