クラスタ化テーブルのクエリ

BigQuery でクラスタ化テーブルを作成すると、テーブルのスキーマ内の 1 つ以上の列のコンテンツに基づいて、テーブルデータが自動的に分類されます。指定した列は、関連するデータを同じ場所に配置するために使用されます。複数の列を使用してテーブルをクラスタ化する場合は、指定する列の順序が重要です。指定した列の順序によって、データの並べ替え順序が決まります。

クラスタ化テーブルに対してクエリを実行するときのパフォーマンスを最適化するには、クラスタ化列を指定した際の順序で、1 つまたは複数のクラスタ化列をフィルタリングする式を使用します。通常、クラスタ化列でフィルタリングするクエリは、非クラスタ化列のみでフィルタリングするクエリよりも優れたパフォーマンスを発揮します。

BigQuery は、クラスタリング列の値に基づいてクラスタ化テーブルのデータを並べ替えて、データをブロックに整理します。

クラスタ化列のフィルタを含むクエリを送信すると、BigQuery はクラスタリング情報を使用して、クエリに関連するデータがブロックに含まれているかどうかを効率的に判断します。これにより、BigQuery は関連するブロックのみをスキャンできます。このプロセスをブロック プルーニングと呼びます。

次の方法で、クラスタ化テーブルにクエリを実行できます。

  • Google Cloud コンソールを使用する
  • bq コマンドライン ツールの bq query コマンドを使用する
  • jobs.insert API メソッドを呼び出してクエリジョブを構成する
  • クライアント ライブラリを使用する

現在、クラスタ化テーブルでは GoogleSQL のみを使用できます。

Go

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

BigQuery に対する認証を行うには、アプリケーションのデフォルト認証情報を設定します。詳細については、ローカル開発環境の認証を設定するをご覧ください。

import (
	"context"
	"fmt"
	"io"

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

// queryClusteredTable demonstrates querying a table that has a clustering specification.
func queryClusteredTable(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)
	}
	defer client.Close()

	q := client.Query(fmt.Sprintf(`
	SELECT
	  COUNT(1) as transactions,
	  SUM(amount) as total_paid,
	  COUNT(DISTINCT destination) as distinct_recipients
    FROM
	  `+"`%s.%s`"+`
	 WHERE
	    timestamp > TIMESTAMP('2015-01-01')
		AND origin = @wallet`, datasetID, tableID))
	q.Parameters = []bigquery.QueryParameter{
		{
			Name:  "wallet",
			Value: "wallet00001866cb7e0f09a890",
		},
	}
	// Run the query and print results when the query job is completed.
	job, err := q.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
	}
	it, err := job.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
}

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.QueryJobConfiguration;
import com.google.cloud.bigquery.TableResult;

public class QueryClusteredTable {

  public static void runQueryClusteredTable() 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";
    queryClusteredTable(projectId, datasetName, tableName);
  }

  public static void queryClusteredTable(String projectId, String datasetName, String tableName) {
    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();

      String sourceTable = "`" + projectId + "." + datasetName + "." + tableName + "`";
      String query =
          "SELECT word, word_count\n"
              + "FROM "
              + sourceTable
              + "\n"
              // Optimize query performance by filtering the clustered columns in sort order
              + "WHERE corpus = 'romeoandjuliet'\n"
              + "AND word_count >= 1";

      QueryJobConfiguration queryConfig = QueryJobConfiguration.newBuilder(query).build();

      TableResult results = bigquery.query(queryConfig);

      results
          .iterateAll()
          .forEach(row -> row.forEach(val -> System.out.printf("%s,", val.toString())));

      System.out.println("Query clustered table performed successfully.");
    } catch (BigQueryException | InterruptedException e) {
      System.out.println("Query not performed \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"

sql = "SELECT * FROM `bigquery-public-data.samples.shakespeare`"
cluster_fields = ["corpus"]

job_config = bigquery.QueryJobConfig(
    clustering_fields=cluster_fields, destination=table_id
)

# Start the query, passing in the extra configuration.
query_job = client.query(sql, job_config=job_config)  # Make an API request.
query_job.result()  # Wait for the job to complete.

table = client.get_table(table_id)  # Make an API request.
if table.clustering_fields == cluster_fields:
    print(
        "The destination table is written using the cluster_fields configuration."
    )

必要な権限

クエリジョブを実行するには、ジョブを実行するプロジェクトに対する bigquery.jobs.create Identity and Access Management(IAM)権限が必要です。

以下の IAM 事前定義ロールには、クエリジョブの実行に必要な権限が含まれています。

  • roles/bigquery.admin
  • roles/bigquery.jobUser
  • roles/bigquery.user

また、クエリで参照するすべてのテーブルとビューに対する bigquery.tables.getData 権限も必要です。ビューをクエリする場合は、基盤となるすべてのテーブルとビューに対してこの権限が必要になります。ただし、承認済みビューまたは承認済みデータセットを使用する場合は、基盤となるソースデータにアクセスする必要はありません。

次の IAM 事前定義ロールには、クエリで参照するすべてのテーブルとビューに対して必要な権限が含まれています。

  • roles/bigquery.admin
  • roles/bigquery.dataOwner
  • roles/bigquery.dataEditor
  • roles/bigquery.dataViewer

BigQuery での IAM のロールの詳細については、事前定義ロールと権限をご覧ください。

ベスト プラクティス

クラスタ化テーブルに対するクエリから最高のパフォーマンスを得るには、次のベスト プラクティスを使用します。

ベスト プラクティスの例で使用されているサンプル テーブルは、DDL ステートメントを使用して作成されたクラスタ化テーブルです。この DDL ステートメントは、ClusteredSalesData という名前のテーブルを作成します。テーブルは、customer_idproduct_idorder_id 列の並べ替え順序でクラスタ化されます。

CREATE TABLE
  `mydataset.ClusteredSalesData`
PARTITION BY
  DATE(timestamp)
CLUSTER BY
  customer_id,
  product_id,
  order_id AS
SELECT
  *
FROM
  `mydataset.SalesData`

クラスタ化列を並べ替え順序でフィルタリングする

フィルタを指定するときは、クラスタ化列を並べ替え順序でフィルタリングする式を使用します。並べ替え順序は、CLUSTER BY 句で指定された列の順序です。クラスタリングの利点を得るには、クラスタ化されたすべての列または列のサブセットを左から右の並べ替え順序で含めます(最初の列から始めます)。たとえば、列の並べ替え順序が ABC の場合、AB でフィルタリングするクエリはクラスタリングの恩恵を受ける可能性がありますが、BC でフィルタリングするクエリはそうなりません。フィルタ式内の列名の順序は、パフォーマンスに影響しません。

次の例では、前の例で作成した ClusteredSalesData クラスタ化テーブルをクエリします。クエリには、customer_id でフィルタリングし、次に product_id でフィルタリングするフィルタ式が含まれています。このクエリは、クラスタ化列を並べ替え順序でフィルタリングしているため、CLUSTER BY 句で指定された列の順序でフィルタリングすることでパフォーマンスが最適化されます。

SELECT
  SUM(totalSale)
FROM
  `mydataset.ClusteredSalesData`
WHERE
  customer_id = 10000
  AND product_id LIKE 'gcp_analytics%'

次のクエリは、フィルタリングにおいてクラスタ化列が並べ替え順序になっていません。その結果、このクエリのパフォーマンスは最適化されません。このクエリでは、最初に product_id でフィルタリングされ、次に order_id でフィルタリングされます(customer_id はスキップしています)。

SELECT
  SUM(totalSale)
FROM
  `mydataset.ClusteredSalesData`
WHERE
  product_id LIKE 'gcp_analytics%'
  AND order_id = 20000

複雑なフィルタ式ではクラスタ化列を使用しない

複雑なフィルタ式でクラスタ化列を使用すると、ブロック プルーニングを適用できないため、クエリのパフォーマンスが最適化されません。

たとえば、次のクエリは、クラスタ化列(customer_id)がフィルタ式の関数の中で使用されているため、ブロック プルーニングが行われません。

SELECT
  SUM(totalSale)
FROM
  `mydataset.ClusteredSalesData`
WHERE
  CAST(customer_id AS STRING) = "10000"

ブロック プルーニングによってクエリのパフォーマンスを最適化するには、次のような単純なフィルタ式を使用します。この例では、単純なフィルタがクラスタ化列(customer_id)に適用されます。

SELECT
  SUM(totalSale)
FROM
  `mydataset.ClusteredSalesData`
WHERE
  customer_id = 10000

クラスタ化列を他の列と比較しない

フィルタ式でクラスタ化列を他の列(クラスタ化列、非クラスタ化列のいずれでも)と比較する場合、ブロック プルーニングを適用できないため、クエリのパフォーマンスは最適化されません。

次のクエリでは、フィルタ式でクラスタ化列(customer_id)と他の列(order_id)を比較しているため、ブロック プルーニングが行われません。

SELECT
  SUM(totalSale)
FROM
  `mydataset.ClusteredSalesData`
WHERE
  customer_id = order_id

テーブルのセキュリティ

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

次のステップ