テーブルデータのページ分割

このドキュメントでは、BigQuery REST API を使用してテーブルデータとクエリ結果のページ分割を行う方法について説明します。

API を使用した結果のページ分割

すべての *collection*.list メソッドから返される結果は、特定の状況においてページ分けされます。ページあたりの結果の数は、maxResults プロパティによって制御されます。

メソッド ページ分け基準 maxResults のデフォルト値 maxResults の最大値
Tabledata.list レスポンス サイズがシリアル化された JSON で 10 MB を超える場合、または maxResults 行を超える場合は、ページ分割された結果を返します。 100,000 100,000
その他すべての *collection*.list メソッド レスポンスが maxResults 行を超える場合、ページ分割された結果を返します。 50 1,000

maxResults を上記の最大値より大きい値に設定すると、結果は最大値に基づいてページ分けされます。

ページは全行数のサブセットです。結果が 1 ページ分のデータを超える場合、結果データには pageToken プロパティが追加されます。次のページの結果を取得するには、別の list 呼び出しを行い、トークン値を pageToken という名前の URL パラメータとして指定します。

テーブルデータのページ分割に使用する tabledata.list メソッドでは、行オフセット値またはページトークンを使用します。詳細については、テーブルデータの閲覧をご覧ください。

BigQuery テーブルデータをページ分割するサンプルを以下に示します。

C#

このサンプルを試す前に、BigQuery クイックスタート: クライアント ライブラリの使用の C# の設定手順を実施してください。詳細については、BigQuery C# API のリファレンス ドキュメントをご覧ください。


using Google.Api.Gax;
using Google.Apis.Bigquery.v2.Data;
using Google.Cloud.BigQuery.V2;
using System;
using System.Collections.Generic;
using System.Linq;

public class BigQueryBrowseTable
{
    public void BrowseTable(
        string projectId = "your-project-id"
    )
    {
        BigQueryClient client = BigQueryClient.Create(projectId);
        TableReference tableReference = new TableReference()
        {
            TableId = "shakespeare",
            DatasetId = "samples",
            ProjectId = "bigquery-public-data"
        };
        // Load all rows from a table
        PagedEnumerable<TableDataList, BigQueryRow> result = client.ListRows(
            tableReference: tableReference,
            schema: null
        );
        // Print the first 10 rows
        foreach (BigQueryRow row in result.Take(10))
        {
            Console.WriteLine($"{row["corpus"]}: {row["word_count"]}");
        }
    }
}

Java

このサンプルを試す前に、BigQuery クイックスタート: クライアント ライブラリの使用の Java の設定手順を実施してください。詳細については、BigQuery Java API のリファレンス ドキュメントをご覧ください。

TableId tableIdObject = TableId.of(datasetName, tableName);
// This example reads the result 100 rows per RPC call. If there's no need to limit the number,
// simply omit the option.
TableResult tableData =
    bigquery.listTableData(tableIdObject, TableDataListOption.pageSize(100));
for (FieldValueList row : tableData.iterateAll()) {
  // do something with the row
}

Go

このサンプルを試す前に、BigQuery クイックスタート: クライアント ライブラリの使用の Go の設定手順を実施してください。詳細については、BigQuery Go API のリファレンス ドキュメントをご覧ください。

Go 用の Google Cloud クライアント ライブラリでは、デフォルトで自動的にページ分けされるため、ページ分けを自分で実装する必要はありません。次に例を示します。

import (
	"context"
	"fmt"
	"io"

	"cloud.google.com/go/bigquery"
	"google.golang.org/api/iterator"
)

// browseTable demonstrates reading data from a BigQuery table directly without the use of a query.
// For large tables, we also recommend the BigQuery Storage API.
func browseTable(w io.Writer, 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)
	}

	table := client.Dataset(datasetID).Table(tableID)
	it := table.Read(ctx)
	for {
		var row []bigquery.Value
		err := it.Next(&row)
		if err == iterator.Done {
			break
		}
		if err != nil {
			return err
		}
		fmt.Fprintln(w, row)
	}
	return nil
}

Node.js

このサンプルを試す前に、BigQuery クイックスタート: クライアント ライブラリの使用の Node.js の設定手順を実施してください。詳細については、BigQuery Node.js API のリファレンス ドキュメントをご覧ください。

Node.js 用の Google Cloud クライアント ライブラリでは、デフォルトで自動的にページ分けされるため、ページ分けを自分で実装する必要はありません。次に例を示します。


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

async function browseRows() {
  // Displays rows from "my_table" in "my_dataset".

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

  // List rows in the table
  const [rows] = await bigquery
    .dataset(datasetId)
    .table(tableId)
    .getRows();

  console.log('Rows:');
  rows.forEach(row => console.log(row));
}

PHP

このサンプルを試す前に、BigQuery クイックスタート: クライアント ライブラリの使用の PHP の設定手順を実施してください。詳細については、BigQuery PHP API のリファレンス ドキュメントをご覧ください。

PHP 用の Google Cloud クライアント ライブラリでジェネレータ関数 rows を使用すると、ページ分けが自動的に発生します。この関数はイテレーション中に結果の次のページをフェッチします。

use Google\Cloud\BigQuery\BigQueryClient;

/** Uncomment and populate these variables in your code */
// $projectId = 'The Google project ID';
// $datasetId = 'The BigQuery dataset ID';
// $tableId   = 'The BigQuery table ID';
// $maxResults = 10;

$maxResults = 10;
$startIndex = 0;

$options = [
    'maxResults' => $maxResults,
    'startIndex' => $startIndex
];
$bigQuery = new BigQueryClient([
    'projectId' => $projectId,
]);
$dataset = $bigQuery->dataset($datasetId);
$table = $dataset->table($tableId);
$numRows = 0;
foreach ($table->rows($options) as $row) {
    print('---');
    foreach ($row as $column => $value) {
        printf('%s: %s' . PHP_EOL, $column, $value);
    }
    $numRows++;
}

Python

このサンプルを試す前に、BigQuery クイックスタート: クライアント ライブラリの使用の Python の設定手順を実施してください。詳細については、BigQuery Python API のリファレンス ドキュメントをご覧ください。

Python 用の Google Cloud クライアント ライブラリでは、デフォルトで自動的にページ分けされるため、ページ分けを自分で実装する必要はありません。次に例を示します。

# TODO(developer): Import the client library.
# from google.cloud import bigquery

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

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

# Download all rows from a table.
rows_iter = client.list_rows(table_id)  # Make an API request.

# Iterate over rows to make the API requests to fetch row data.
rows = list(rows_iter)
print("Downloaded {} rows from table {}".format(len(rows), table_id))

# Download at most 10 rows.
rows_iter = client.list_rows(table_id, max_results=10)
rows = list(rows_iter)
print("Downloaded {} rows from table {}".format(len(rows), table_id))

# Specify selected fields to limit the results to certain columns.
table = client.get_table(table_id)  # Make an API request.
fields = table.schema[:2]  # First two columns.
rows_iter = client.list_rows(table_id, selected_fields=fields, max_results=10)
rows = list(rows_iter)
print("Selected {} columns from table {}.".format(len(rows_iter.schema), table_id))
print("Downloaded {} rows from table {}".format(len(rows), table_id))

# Print row data in tabular format.
rows = client.list_rows(table, max_results=10)
format_string = "{!s:<16} " * len(rows.schema)
field_names = [field.name for field in rows.schema]
print(format_string.format(*field_names))  # Prints column headers.
for row in rows:
    print(format_string.format(*row))  # Prints row data.

Ruby

このサンプルを試す前に、BigQuery クイックスタート: クライアント ライブラリの使用の Ruby の設定手順を実施してください。詳細については、BigQuery Ruby API のリファレンス ドキュメントをご覧ください。

Ruby 用 Google Cloud クライアント ライブラリTable#dataData#next を使用すると、ページ分割が自動的に行われます。

require "google/cloud/bigquery"

def browse_table
  bigquery = Google::Cloud::Bigquery.new project_id: "bigquery-public-data"
  dataset  = bigquery.dataset "samples"
  table    = dataset.table "shakespeare"

  # Load all rows from a table
  rows = table.data

  # Load the first 10 rows
  rows = table.data max: 10

  # Print row data
  rows.each { |row| puts row }
end

任意のページのリクエストと、冗長リスト呼び出しの回避

前のページに戻る場合、またはキャッシュされた pageToken の値を使用して任意のページにジャンプする場合、ページのデータが最後に表示された後で変更されている可能性がありますが、データが変更されたことは明確には示されません。これを確認するには、ETag プロパティを使用できます。

すべての collection.list メソッド(Tabledata 用を除く)は、結果で Etag プロパティを返します。このプロパティは、ページが最後のリクエスト以降に変更されているかどうかを確認するために使用できるページ結果のハッシュです。ETag 値を使用して BigQuery にリクエストを行うと、BigQuery はその ETag 値と API によって返された ETag 値を比較し、ETag 値が一致しているかどうかに基づいて応答します。次の方法で、冗長なリスト呼び出しを回避するために ETag を使用できます。

  • 値が変更されている場合にのみリスト値を返す場合:

    値が変更されている場合にのみリスト値のページを返す場合は、HTTP の If-None-Match ヘッダーを使用し、保存しておいた ETag を指定してリスト呼び出しを行うことができます。指定した ETag がサーバー上の ETag と一致しない場合、BigQuery は、新しいリスト値のページを返します。ETag が一致する場合、BigQuery は HTTP 304「Not Modified」という結果を返し、値を返しません。この例として、ユーザーが BigQuery に格納されている情報を定期的に入力するウェブページがあります。ETag と If-None-Match ヘッダーを使用することにより、データに変更がない場合に冗長なリスト呼び出しを避けることができます。

  • 値が変更されていない場合にのみリスト値を返す場合:

    リストの値が変更されていない場合にのみリスト値のページを返す場合は、HTTP の If-Match ヘッダーを使用できます。BigQuery は ETag 値を比較し、結果が変更されていない場合は結果のページを返します。ページが変更されている場合は、412「Precondition Failed」という結果を返します。

注: ETag は冗長なリスト呼び出しを避けるために有効な方法ですが、同じ方法を使用して、いずれかのオブジェクトが変更されたかどうかを確認できます。たとえば、特定のテーブルに対する get リクエストを実行し、完全なレスポンスが返される前に、ETag を使用して、テーブルが変更されているかどうかを判断できます。

クエリ結果のページ分割

各クエリは宛先テーブルに書き込みます。宛先テーブルが指定されていない場合、BigQuery API は宛先テーブル プロパティに一時匿名テーブルへの参照を自動的に挿入します。

API

jobs.config.query.destinationTable フィールドを読み取り、クエリ結果が書き込まれたテーブルを確認します。クエリ結果を読み取るには、tabledata.list を呼び出します。

Python

QueryJob.result メソッドは、クエリ結果のイテラブルを返します。または

  1. QueryJob.destination プロパティを読み取ります。構成されていない場合、API がこのプロパティに一時匿名テーブルへの参照を設定します。
  2. Client.get_table メソッドでテーブル スキーマを取得します。
  3. Client.list_rows メソッドを使用して、宛先テーブルのすべての行に対するイテラブルを作成します。

このサンプルを試す前に、BigQuery クイックスタート: クライアント ライブラリの使用の Python の設定手順を実施してください。詳細については、BigQuery Python API のリファレンス ドキュメントをご覧ください。

# TODO(developer): Import the client library.
# from google.cloud import bigquery

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

query = """
    SELECT name, SUM(number) as total_people
    FROM `bigquery-public-data.usa_names.usa_1910_2013`
    GROUP BY name
    ORDER BY total_people DESC
"""
query_job = client.query(query)  # Make an API request.
query_job.result()  # Wait for the query to complete.

# Get the destination table for the query results.
#
# All queries write to a destination table. If a destination table is not
# specified, the BigQuery populates it with a reference to a temporary
# anonymous table after the query completes.
destination = query_job.destination

# Get the schema (and other properties) for the destination table.
#
# A schema is useful for converting from BigQuery types to Python types.
destination = client.get_table(destination)

# Download rows.
#
# The client library automatically handles pagination.
print("The query data:")
rows = client.list_rows(destination, max_results=20)
for row in rows:
    print("name={}, count={}".format(row["name"], row["total_people"]))