Google クライアントライブラリ for Python で、BigQuery のデータセットやテーブルなどのメタ情報を取得してみた

2020.04.16

こんにちは、みかみです。

BigQuery のデータセットやテーブル、UDF などの情報は、INFORMATION_SCHEMA の各ビューから取得できますが、オンデマンド料金の場合、現在のところ INFORMATION_SCHEMA に対して SELECT 文を実行するとクエリ課金が発生します。

一方、list、get などのメタデータオペレーションは無料で使用でき、課金は発生しません。

Google クライアントライブラリ for Python ( google-cloud-bigquery )を使用して、BigQuery REST API 経由ではどんなリソース情報が取得できるか確認してみました。

やりたいこと

  • Google クライアントライブラリ for Python ( google-cloud-bigquery )でデータセットなどのリソース情報を取得する方法を知りたい
  • クライアントライブラリで取得できるリソース情報の詳細項目を知りたい
  • クライアントライブラリと INFORMATION_SCHEMA で取得できる情報には差分があるのかどうか知りたい

目次

前提

動作確認に使用した クライアントライブラリ のバージョンは以下です。

(test_bq) [ec2-user@ip-10-0-43-239 get_info]$ pip list | grep google-cloud-bigquery
google-cloud-bigquery    1.24.0

データセット情報

クライアントライブラリの以下のメソッドでデータセット情報を取得できます

  • list_datasets:プロジェクト内のデータセット一覧
  • get_dataset:データセットの詳細情報

list_datasets で取得できる項目を確認

以下の Python コードで、list_datasets() の戻り値を print して確認してみます。

from google.cloud import bigquery
from pprint import pprint

client = bigquery.Client()

project_id = 'cm-da-mikami-yuki-258308'
datasets = client.list_datasets(project_id)
if datasets:
    for obj in datasets:
        print('-------->')
        pprint(vars(obj))
else:
    print("This project does not contain any datasets.")
(test_bq) [ec2-user@ip-10-0-43-239 get_info]$ python get_dataset_info.py
-------->
{'_properties': {'datasetReference': {'datasetId': 'airflow_test',
                                      'projectId': 'cm-da-mikami-yuki-258308'},
                 'id': 'cm-da-mikami-yuki-258308:airflow_test',
                 'kind': 'bigquery#dataset',
                 'location': 'US'}}
-------->
{'_properties': {'datasetReference': {'datasetId': 'billing',
                                      'projectId': 'cm-da-mikami-yuki-258308'},
                 'id': 'cm-da-mikami-yuki-258308:billing',
                 'kind': 'bigquery#dataset',
                 'location': 'asia-northeast1'}}
-------->
{'_properties': {'datasetReference': {'datasetId': 'dataset_1',
                                      'projectId': 'cm-da-mikami-yuki-258308'},
                 'id': 'cm-da-mikami-yuki-258308:dataset_1',
                 'kind': 'bigquery#dataset',
                 'labels': {'env': 'test'},
                 'location': 'asia-northeast1'}}
-------->
{'_properties': {'datasetReference': {'datasetId': 'dataset_2',
                                      'projectId': 'cm-da-mikami-yuki-258308'},
                 'id': 'cm-da-mikami-yuki-258308:dataset_2',
                 'kind': 'bigquery#dataset',
                 'location': 'asia-northeast1'}}
-------->
{'_properties': {'datasetReference': {'datasetId': 'test_dataset_option_all',
                                      'projectId': 'cm-da-mikami-yuki-258308'},
                 'friendlyName': 'オプション確認用',
                 'id': 'cm-da-mikami-yuki-258308:test_dataset_option_all',
                 'kind': 'bigquery#dataset',
                 'labels': {'option': 'all'},
                 'location': 'asia-northeast1'}}
  • リファレンス情報(プロジェクト ID、データセット ID )
  • データセット ID
  • リソース種別( bigquery#dataset
  • ロケーション
  • ラベル情報
  • friendly name

が取得できました。

get_dataset で取得できる項目を確認

続いて、get_dataset() メソッドの戻り値を確認するため、Python コードを以下に書き換えて実行してみます。

from google.cloud import bigquery
from pprint import pprint

client = bigquery.Client()

project_id = 'cm-da-mikami-yuki-258308'
datasets = client.list_datasets(project_id)
if datasets:
    for obj in datasets:
        print('-------->')
#        pprint(vars(obj))
        dataset = client.get_dataset(obj.reference)
        pprint(vars(dataset))
else:
    print("This project does not contain any datasets.")
(test_bq) [ec2-user@ip-10-0-43-239 get_info]$ python get_dataset_info.py
-------->
{'_properties': {'access': [{'role': 'WRITER',
                             'specialGroup': 'projectWriters'},
                            {'role': 'OWNER', 'specialGroup': 'projectOwners'},
                            {'role': 'OWNER',
                             'userByEmail': 'xxxx@classmethod.jp'},
                            {'role': 'READER',
                             'specialGroup': 'projectReaders'}],
                 'creationTime': '1584102236040',
                 'datasetReference': {'datasetId': 'airflow_test',
                                      'projectId': 'cm-da-mikami-yuki-258308'},
                 'etag': 'edTJxxxxcQ==',
                 'id': 'cm-da-mikami-yuki-258308:airflow_test',
                 'kind': 'bigquery#dataset',
                 'lastModifiedTime': '1584102236040',
                 'location': 'US',
                 'selfLink': 'https://bigquery.googleapis.com/bigquery/v2/projects/cm-da-mikami-yuki-258308/datasets/airflow_test'}}
-------->
{'_properties': {'access': [{'role': 'WRITER',
                             'specialGroup': 'projectWriters'},
                            {'role': 'OWNER', 'specialGroup': 'projectOwners'},
                            {'role': 'OWNER',
                             'userByEmail': 'billing-export-bigquery@system.gserviceaccount.com'},
                            {'role': 'OWNER',
                             'userByEmail': 'xxxx@classmethod.jp'},
                            {'role': 'READER',
                             'specialGroup': 'projectReaders'}],
                 'creationTime': '1585283075708',
                 'datasetReference': {'datasetId': 'billing',
                                      'projectId': 'cm-da-mikami-yuki-258308'},
                 'etag': 'xNPkxxxxeQ==',
                 'id': 'cm-da-mikami-yuki-258308:billing',
                 'kind': 'bigquery#dataset',
                 'lastModifiedTime': '1585283726009',
                 'location': 'asia-northeast1',
                 'selfLink': 'https://bigquery.googleapis.com/bigquery/v2/projects/cm-da-mikami-yuki-258308/datasets/billing'}}
-------->
{'_properties': {'access': [{'role': 'WRITER',
                             'specialGroup': 'projectWriters'},
                            {'role': 'WRITER',
                             'userByEmail': 'service-797147019523@gcp-sa-bigquerydatatransfer.iam.gserviceaccount.com'},
                            {'role': 'OWNER', 'specialGroup': 'projectOwners'},
                            {'role': 'OWNER',
                             'userByEmail': 'xxxx@classmethod.jp'},
                            {'role': 'READER',
                             'specialGroup': 'projectReaders'},
                            {'role': 'READER',
                             'userByEmail': 'test-mikami-account-c@cm-da-mikami-yuki-258308.iam.gserviceaccount.com'}],
                 'creationTime': '1585896779508',
                 'datasetReference': {'datasetId': 'dataset_1',
                                      'projectId': 'cm-da-mikami-yuki-258308'},
                 'etag': 'xDnMxxxxYQ==',
                 'id': 'cm-da-mikami-yuki-258308:dataset_1',
                 'kind': 'bigquery#dataset',
                 'labels': {'env': 'test'},
                 'lastModifiedTime': '1586516882711',
                 'location': 'asia-northeast1',
                 'selfLink': 'https://bigquery.googleapis.com/bigquery/v2/projects/cm-da-mikami-yuki-258308/datasets/dataset_1'}}
-------->
{'_properties': {'access': [{'role': 'WRITER',
                             'specialGroup': 'projectWriters'},
                            {'role': 'OWNER', 'specialGroup': 'projectOwners'},
                            {'role': 'OWNER',
                             'userByEmail': 'xxxx@classmethod.jp'},
                            {'role': 'READER',
                             'specialGroup': 'projectReaders'}],
                 'creationTime': '1585896793308',
                 'datasetReference': {'datasetId': 'dataset_2',
                                      'projectId': 'cm-da-mikami-yuki-258308'},
                 'description': '説明付きのデータセット',
                 'etag': 'bVufxxxxqg==',
                 'id': 'cm-da-mikami-yuki-258308:dataset_2',
                 'kind': 'bigquery#dataset',
                 'lastModifiedTime': '1586405357740',
                 'location': 'asia-northeast1',
                 'selfLink': 'https://bigquery.googleapis.com/bigquery/v2/projects/cm-da-mikami-yuki-258308/datasets/dataset_2'}}
-------->
{'_properties': {'access': [{'role': 'WRITER',
                             'specialGroup': 'projectWriters'},
                            {'role': 'OWNER', 'specialGroup': 'projectOwners'},
                            {'role': 'OWNER',
                             'userByEmail': 'test-mikami@cm-da-mikami-yuki-258308.iam.gserviceaccount.com'},
                            {'role': 'READER',
                             'specialGroup': 'projectReaders'}],
                 'creationTime': '1586419321609',
                 'datasetReference': {'datasetId': 'test_dataset_option_all',
                                      'projectId': 'cm-da-mikami-yuki-258308'},
                 'defaultTableExpirationMs': '7776000000',
                 'description': 'オプション情報取得確認用のデータセット',
                 'etag': 'dYBlxxxx3w==',
                 'friendlyName': 'オプション確認用',
                 'id': 'cm-da-mikami-yuki-258308:test_dataset_option_all',
                 'kind': 'bigquery#dataset',
                 'labels': {'option': 'all'},
                 'lastModifiedTime': '1586419430241',
                 'location': 'asia-northeast1',
                 'selfLink': 'https://bigquery.googleapis.com/bigquery/v2/projects/cm-da-mikami-yuki-258308/datasets/test_dataset_option_all'}}

※一部、伏字に変更しています。

list_datasets で取得できる項目に加えて、以下の情報も取得できました。

  • アクセス制御情報
  • 作成日時(Unixtime)
  • デフォルトのテーブルの有効期限
  • description
  • ETag 値
  • 最終更新日時(Unixtime)
  • API リクエスト URL

テーブル / ビュー情報

テーブル / ビュー情報の取得には、以下のメソッドを使用します。

  • list_tables:データセット内のテーブル / ビュー一覧
  • get_table:テーブル / ビューの詳細情報

list_tables で取得できる項目を確認

list_tables で特定のデータセット内のテーブルの情報を取得してみます。

from google.cloud import bigquery
from pprint import pprint

client = bigquery.Client()

dataset_id = 'cm-da-mikami-yuki-258308.dataset_1'
dataset = client.get_dataset(dataset_id)
tables = client.list_tables(dataset)
if tables:
    for obj in tables:
        print('-------->')
        pprint(vars(obj))
else:
    print("\tThis dataset does not contain any tables.")
(test_bq) [ec2-user@ip-10-0-43-239 get_info]$ python get_table_info.py
-------->
{'_properties': {'creationTime': '1586855793633',
                 'id': 'cm-da-mikami-yuki-258308:dataset_1.pos_partition_date',
                 'kind': 'bigquery#table',
                 'tableReference': {'datasetId': 'dataset_1',
                                    'projectId': 'cm-da-mikami-yuki-258308',
                                    'tableId': 'pos_partition_date'},
                 'timePartitioning': {'field': 'first_date', 'type': 'DAY'},
                 'type': 'TABLE'}}
-------->
{'_properties': {'creationTime': '1586853331458',
                 'id': 'cm-da-mikami-yuki-258308:dataset_1.pos_partition_loadtime',
                 'kind': 'bigquery#table',
                 'tableReference': {'datasetId': 'dataset_1',
                                    'projectId': 'cm-da-mikami-yuki-258308',
                                    'tableId': 'pos_partition_loadtime'},
                 'timePartitioning': {'type': 'DAY'},
                 'type': 'TABLE'}}
-------->
{'_properties': {'creationTime': '1586857582005',
                 'id': 'cm-da-mikami-yuki-258308:dataset_1.pos_partition_loadtime_with_filter',
                 'kind': 'bigquery#table',
                 'tableReference': {'datasetId': 'dataset_1',
                                    'projectId': 'cm-da-mikami-yuki-258308',
                                    'tableId': 'pos_partition_loadtime_with_filter'},
                 'timePartitioning': {'requirePartitionFilter': True,
                                      'type': 'DAY'},
                 'type': 'TABLE'}}
-------->
{'_properties': {'creationTime': '1586856044131',
                 'id': 'cm-da-mikami-yuki-258308:dataset_1.pos_partition_val',
                 'kind': 'bigquery#table',
                 'rangePartitioning': {'field': 'JANCD',
                                       'range': {'end': '5000000000000',
                                                 'interval': '100000000',
                                                 'start': '4000000000000'}},
                 'tableReference': {'datasetId': 'dataset_1',
                                    'projectId': 'cm-da-mikami-yuki-258308',
                                    'tableId': 'pos_partition_val'},
                 'type': 'TABLE'}}
-------->
{'_properties': {'creationTime': '1585897296621',
                 'id': 'cm-da-mikami-yuki-258308:dataset_1.table_dogs',
                 'kind': 'bigquery#table',
                 'labels': {'type': 'dog'},
                 'tableReference': {'datasetId': 'dataset_1',
                                    'projectId': 'cm-da-mikami-yuki-258308',
                                    'tableId': 'table_dogs'},
                 'type': 'TABLE'}}
-------->
{'_properties': {'creationTime': '1586430585167',
                 'id': 'cm-da-mikami-yuki-258308:dataset_1.table_dogs_nested',
                 'kind': 'bigquery#table',
                 'tableReference': {'datasetId': 'dataset_1',
                                    'projectId': 'cm-da-mikami-yuki-258308',
                                    'tableId': 'table_dogs_nested'},
                 'type': 'TABLE'}}
-------->
{'_properties': {'creationTime': '1587015388951',
                 'expirationTime': '1590935802000',
                 'id': 'cm-da-mikami-yuki-258308:dataset_1.table_expiring',
                 'kind': 'bigquery#table',
                 'tableReference': {'datasetId': 'dataset_1',
                                    'projectId': 'cm-da-mikami-yuki-258308',
                                    'tableId': 'table_expiring'},
                 'type': 'TABLE'}}
-------->
{'_properties': {'creationTime': '1586778384776',
                 'id': 'cm-da-mikami-yuki-258308:dataset_1.table_external',
                 'kind': 'bigquery#table',
                 'tableReference': {'datasetId': 'dataset_1',
                                    'projectId': 'cm-da-mikami-yuki-258308',
                                    'tableId': 'table_external'},
                 'type': 'EXTERNAL'}}
-------->
{'_properties': {'creationTime': '1586424010456',
                 'friendlyName': '分かりやすい名前付きのテーブル',
                 'id': 'cm-da-mikami-yuki-258308:dataset_1.table_friendly_name',
                 'kind': 'bigquery#table',
                 'tableReference': {'datasetId': 'dataset_1',
                                    'projectId': 'cm-da-mikami-yuki-258308',
                                    'tableId': 'table_friendly_name'},
                 'type': 'TABLE'}}
-------->
{'_properties': {'creationTime': '1586422712110',
                 'id': 'cm-da-mikami-yuki-258308:dataset_1.table_kms',
                 'kind': 'bigquery#table',
                 'tableReference': {'datasetId': 'dataset_1',
                                    'projectId': 'cm-da-mikami-yuki-258308',
                                    'tableId': 'table_kms'},
                 'type': 'TABLE'}}
-------->
{'_properties': {'creationTime': '1586425128064',
                 'id': 'cm-da-mikami-yuki-258308:dataset_1.table_partition_expiring',
                 'kind': 'bigquery#table',
                 'tableReference': {'datasetId': 'dataset_1',
                                    'projectId': 'cm-da-mikami-yuki-258308',
                                    'tableId': 'table_partition_expiring'},
                 'timePartitioning': {'expirationMs': '36000000',
                                      'type': 'DAY'},
                 'type': 'TABLE'}}
-------->
{'_properties': {'creationTime': '1587015640942',
                 'id': 'cm-da-mikami-yuki-258308:dataset_1.view_dogs',
                 'kind': 'bigquery#table',
                 'tableReference': {'datasetId': 'dataset_1',
                                    'projectId': 'cm-da-mikami-yuki-258308',
                                    'tableId': 'view_dogs'},
                 'type': 'VIEW',
                 'view': {'useLegacySql': False}}}
-------->
{'_properties': {'creationTime': '1586518663164',
                 'friendlyName': 'フレンドリーネーム',
                 'id': 'cm-da-mikami-yuki-258308:dataset_1.view_dogs_2',
                 'kind': 'bigquery#table',
                 'labels': {'type': 'dog'},
                 'tableReference': {'datasetId': 'dataset_1',
                                    'projectId': 'cm-da-mikami-yuki-258308',
                                    'tableId': 'view_dogs_2'},
                 'type': 'VIEW',
                 'view': {'useLegacySql': False}}}

以下の項目が取得できました。

  • 作成日時(Unixtime)
  • テーブル ID
  • リソース種別( bigquery#table
  • ラベル情報
  • リファレンス情報(プロジェクト ID、データセット ID、テーブル ID )
  • テーブル種別( TABLE or VIEW or EXTERNAL
  • 有効期限(Unixtime)
  • friendly name
  • 分割タイプ、パーティションフィルタ要否
  • パーティション範囲情報
  • パーティションの有効期限(Unixtime)

get_table で取得できる項目を確認

データセット情報同様、get_table に変更して実行してみます。

from google.cloud import bigquery
from pprint import pprint

client = bigquery.Client()

dataset_id = 'cm-da-mikami-yuki-258308.dataset_1'
dataset = client.get_dataset(dataset_id)
tables = client.list_tables(dataset)
if tables:
    for obj in tables:
        print('-------->')
#        pprint(vars(obj))
        table = client.get_table(obj.reference)
        pprint(vars(table))
else:
    print("\tThis dataset does not contain any tables.")
(test_bq) [ec2-user@ip-10-0-43-239 get_info]$ python get_table_info.py
-------->
{'_properties': {'creationTime': '1586855793633',
                 'etag': 'yziexxxxcA==',
                 'id': 'cm-da-mikami-yuki-258308:dataset_1.pos_partition_date',
                 'kind': 'bigquery#table',
                 'lastModifiedTime': '1586855793633',
                 'location': 'asia-northeast1',
                 'numBytes': '14192',
                 'numLongTermBytes': '0',
                 'numRows': '100',
                 'schema': {'fields': [{'mode': 'REQUIRED',
                                        'name': 'id',
                                        'type': 'INTEGER'},
                                       {'mode': 'REQUIRED',
                                        'name': 'JANCD',
                                        'type': 'INTEGER'},
                                       {'mode': 'NULLABLE',
                                        'name': 'maker',
                                        'type': 'STRING'},
                                       {'mode': 'NULLABLE',
                                        'name': 'name',
                                        'type': 'STRING'},
                                       {'mode': 'NULLABLE',
                                        'name': 'first_date',
                                        'type': 'DATE'},
                                       {'mode': 'NULLABLE',
                                        'name': 'PI_amount',
                                        'type': 'INTEGER'},
                                       {'mode': 'NULLABLE',
                                        'name': 'PI_week',
                                        'type': 'STRING'},
                                       {'mode': 'NULLABLE',
                                        'name': 'store_percentage',
                                        'type': 'STRING'},
                                       {'mode': 'NULLABLE',
                                        'name': 'price_avg',
                                        'type': 'INTEGER'}]},
                 'selfLink': 'https://bigquery.googleapis.com/bigquery/v2/projects/cm-da-mikami-yuki-258308/datasets/dataset_1/tables/pos_partition_date',
                 'tableReference': {'datasetId': 'dataset_1',
                                    'projectId': 'cm-da-mikami-yuki-258308',
                                    'tableId': 'pos_partition_date'},
                 'timePartitioning': {'field': 'first_date', 'type': 'DAY'},
                 'type': 'TABLE'}}
-------->
{'_properties': {'creationTime': '1586853331458',
                 'etag': 'f1i3xxxxUQ==',
                 'id': 'cm-da-mikami-yuki-258308:dataset_1.pos_partition_loadtime',
(省略)
                 'timePartitioning': {'type': 'DAY'},
                 'type': 'TABLE'}}
-------->
{'_properties': {'creationTime': '1586857582005',
                 'etag': 'd8edxxxxvA==',
                 'id': 'cm-da-mikami-yuki-258308:dataset_1.pos_partition_loadtime_with_filter',
(省略)
                 'requirePartitionFilter': True,
(省略)
                 'timePartitioning': {'requirePartitionFilter': True,
                                      'type': 'DAY'},
                 'type': 'TABLE'}}
-------->
{'_properties': {'creationTime': '1586856044131',
                 'etag': '2hWLxxxxBg==',
                 'id': 'cm-da-mikami-yuki-258308:dataset_1.pos_partition_val',
(省略)
                 'rangePartitioning': {'field': 'JANCD',
                                       'range': {'end': '5000000000000',
                                                 'interval': '100000000',
                                                 'start': '4000000000000'}},
(省略)
                 'type': 'TABLE'}}
-------->
{'_properties': {'creationTime': '1585897296621',
                 'description': 'テスト用',
                 'etag': 'BXWKxxxx3g==',
                 'id': 'cm-da-mikami-yuki-258308:dataset_1.table_dogs',
                 'kind': 'bigquery#table',
                 'labels': {'type': 'dog'},
(省略)
                 'type': 'TABLE'}}
-------->
{'_properties': {'creationTime': '1586430585167',
                 'etag': '+ipdxxxxdw==',
                 'id': 'cm-da-mikami-yuki-258308:dataset_1.table_dogs_nested',
                 'kind': 'bigquery#table',
                 'lastModifiedTime': '1586430657420',
                 'location': 'asia-northeast1',
                 'numBytes': '0',
                 'numLongTermBytes': '0',
                 'numRows': '0',
                 'schema': {'fields': [{'description': 'Object',
                                        'fields': [{'description': '名前',
                                                    'mode': 'REQUIRED',
                                                    'name': 'name',
                                                    'type': 'STRING'},
                                                   {'description': '性別',
                                                    'name': 'gender',
                                                    'type': 'STRING'},
                                                   {'description': '年齢',
                                                    'name': 'age',
                                                    'type': 'STRING'}],
                                        'mode': 'REPEATED',
                                        'name': 'dog',
                                        'type': 'RECORD'}]},
                 'selfLink': 'https://bigquery.googleapis.com/bigquery/v2/projects/cm-da-mikami-yuki-258308/datasets/dataset_1/tables/table_dogs_nested',
                 'tableReference': {'datasetId': 'dataset_1',
                                    'projectId': 'cm-da-mikami-yuki-258308',
                                    'tableId': 'table_dogs_nested'},
                 'type': 'TABLE'}}
-------->
{'_properties': {'creationTime': '1587015388951',
                 'etag': 'aXNQxxxxow==',
                 'expirationTime': '1590935802000',
                 'id': 'cm-da-mikami-yuki-258308:dataset_1.table_expiring',
(省略)
                 'type': 'TABLE'}}
-------->
{'_properties': {'creationTime': '1586778384776',
                 'etag': 'ad2Zxxxx+Q==',
                 'externalDataConfiguration': {'sourceFormat': 'CSV',
                                               'sourceUris': ['gs://test-mikami/yob1980.txt']},
                 'id': 'cm-da-mikami-yuki-258308:dataset_1.table_external',
(省略)
                 'type': 'EXTERNAL'}}
-------->
{'_properties': {'creationTime': '1586424010456',
                 'etag': 'XsHAxxxxag==',
                 'friendlyName': '分かりやすい名前付きのテーブル',
                 'id': 'cm-da-mikami-yuki-258308:dataset_1.table_friendly_name',
(省略)
                 'type': 'TABLE'}}
-------->
{'_properties': {'creationTime': '1586422712110',
                 'encryptionConfiguration': {'kmsKeyName': 'projects/cm-da-mikami-yuki-258308/locations/asia-northeast1/keyRings/xxxx/cryptoKeys/xxxxi'},
                 'etag': 'uRqMxxxxfg==',
                 'id': 'cm-da-mikami-yuki-258308:dataset_1.table_kms',
(省略)
                 'type': 'TABLE'}}
-------->
{'_properties': {'creationTime': '1586425128064',
                 'etag': 'tAw/xxxx5Q==',
                 'id': 'cm-da-mikami-yuki-258308:dataset_1.table_partition_expiring',
(省略)
                 'timePartitioning': {'expirationMs': '36000000',
                                      'type': 'DAY'},
                 'type': 'TABLE'}}
-------->
{'_properties': {'creationTime': '1587015640942',
                 'etag': 'mWbdxxxxXg==',
                 'id': 'cm-da-mikami-yuki-258308:dataset_1.view_dogs',
(省略)
                 'type': 'VIEW',
                 'view': {'query': 'SELECT id, name FROM '
                                   'cm-da-mikami-yuki-258308.dataset_1.table_dogs',
                          'useLegacySql': False}}}
-------->
{'_properties': {'creationTime': '1586518663164',
                 'description': 'ビューからビュー',
                 'etag': '0Qokxxxx9g==',
                 'friendlyName': 'フレンドリーネーム',
                 'id': 'cm-da-mikami-yuki-258308:dataset_1.view_dogs_2',
                 'kind': 'bigquery#table',
                 'labels': {'type': 'dog'},
                 'lastModifiedTime': '1586518663164',
                 'location': 'asia-northeast1',
                 'numBytes': '0',
                 'numLongTermBytes': '0',
                 'numRows': '0',
                 'schema': {'fields': [{'name': 'name', 'type': 'STRING'}]},
                 'selfLink': 'https://bigquery.googleapis.com/bigquery/v2/projects/cm-da-mikami-yuki-258308/datasets/dataset_1/tables/view_dogs_2',
                 'tableReference': {'datasetId': 'dataset_1',
                                    'projectId': 'cm-da-mikami-yuki-258308',
                                    'tableId': 'view_dogs_2'},
                 'type': 'VIEW',
                 'view': {'query': 'SELECT name FROM '
                                   'cm-da-mikami-yuki-258308.dataset_1.view_dogs',
                          'useLegacySql': False}}}

※一部省略&伏字に変更しています。

list_tables で取得できる項目に加えて、以下の情報が取得できました。

  • テーブル ID
  • データサイズ( byte )
  • 行数
  • テーブルスキーマ情報
  • ロケーション
  • description
  • ロケーション
  • ETag 値
  • 最終更新日時(Unixtime)
  • API のリクエスト URL
  • 外部データソース
  • KMS キー
  • View 定義、View がレガシー SQL で作成されたかどうか

ルーティン情報

UDF やプロシージャの情報は、以下のメソッドで取得できます。

  • list_routines:データセット内のルーティン一覧
  • get_routine:ルーティンの詳細情報

list_routines で取得できる項目を確認

list_routines で UDF、プロシージャの一覧を取得してみます。

from google.cloud import bigquery
from pprint import pprint

client = bigquery.Client()

dataset_id = 'cm-da-mikami-yuki-258308.dataset_1'
dataset = client.get_dataset(dataset_id)
routines = client.list_routines(dataset)
if routines:
    for obj in routines:
        print('-------->')
        pprint(vars(obj))
else:
    print("\tThis dataset does not contain any routines.")
(test_bq) [ec2-user@ip-10-0-43-239 get_info]$ python get_routine_info.py
-------->
{'_properties': {'creationTime': '1586493860018',
                 'etag': 'VEQYxxxxIA==',
                 'language': 'SQL',
                 'lastModifiedTime': '1586493860018',
                 'routineReference': {'datasetId': 'dataset_1',
                                      'projectId': 'cm-da-mikami-yuki-258308',
                                      'routineId': 'addFourAndDivideAny'},
                 'routineType': 'SCALAR_FUNCTION'}}
-------->
{'_properties': {'creationTime': '1586513017763',
                 'etag': 'cx5rxxxxjQ==',
                 'language': 'SQL',
                 'lastModifiedTime': '1586513017763',
                 'routineReference': {'datasetId': 'dataset_1',
                                      'projectId': 'cm-da-mikami-yuki-258308',
                                      'routineId': 'test_function'},
                 'routineType': 'SCALAR_FUNCTION'}}
-------->
{'_properties': {'creationTime': '1586506722471',
                 'etag': '6b9wxxxx2w==',
                 'language': 'SQL',
                 'lastModifiedTime': '1586506722471',
                 'routineReference': {'datasetId': 'dataset_1',
                                      'projectId': 'cm-da-mikami-yuki-258308',
                                      'routineId': 'test_procedure'},
                 'routineType': 'PROCEDURE'}}
-------->
{'_properties': {'creationTime': '1586513263155',
                 'etag': 'bR4RxxxxBw==',
                 'language': 'SQL',
                 'lastModifiedTime': '1586513263155',
                 'routineReference': {'datasetId': 'dataset_1',
                                      'projectId': 'cm-da-mikami-yuki-258308',
                                      'routineId': 'test_procedure_description'},
                 'routineType': 'PROCEDURE'}}
-------->
{'_properties': {'creationTime': '1586508765922',
                 'etag': 'Lrvjxxxx6A==',
                 'language': 'JAVASCRIPT',
                 'lastModifiedTime': '1586508765922',
                 'routineReference': {'datasetId': 'dataset_1',
                                      'projectId': 'cm-da-mikami-yuki-258308',
                                      'routineId': 'timesTwo'},
                 'routineType': 'SCALAR_FUNCTION'}}

※一部、伏字に変更しています。

以下の情報が取得できました。

  • 作成日時(Unixtime)
  • ETag 値
  • UDF 定義言語
  • 最終更新日時(Unixtime)
  • リファレンス情報(プロジェクト ID、データセット ID、ルーティン ID )
  • ルーティン種別( SCALAR_FUNCTION or PROCEDURE

get_routine で取得できる項目を確認

より詳細なルーティン情報は、get_routine で取得します。

from google.cloud import bigquery
from pprint import pprint

client = bigquery.Client()

dataset_id = 'cm-da-mikami-yuki-258308.dataset_1'
dataset = client.get_dataset(dataset_id)
routines = client.list_routines(dataset)
if routines:
    for obj in routines:
        print('-------->')
#        pprint(vars(obj))
        routine = client.get_routine(obj.reference)
        pprint(vars(routine))
else:
    print("\tThis dataset does not contain any routines.")
(test_bq) [ec2-user@ip-10-0-43-239 get_info]$ python get_routine_info.py
-------->
{'_properties': {'arguments': [{'argumentKind': 'ANY_TYPE', 'name': 'x'},
                               {'argumentKind': 'ANY_TYPE', 'name': 'y'}],
                 'creationTime': '1586493860018',
                 'definitionBody': '(x + 4) / y',
                 'etag': 'VEQYxxxxIA==',
                 'language': 'SQL',
                 'lastModifiedTime': '1586493860018',
                 'routineReference': {'datasetId': 'dataset_1',
                                      'projectId': 'cm-da-mikami-yuki-258308',
                                      'routineId': 'addFourAndDivideAny'},
                 'routineType': 'SCALAR_FUNCTION'}}
-------->
{'_properties': {'creationTime': '1586513017763',
                 'definitionBody': '1*2',
                 'etag': 'cx5rxxxxjQ==',
                 'importedLibraries': ['gs://cloud-samples-data/bigquery/udfs/max-value.js'],
                 'language': 'SQL',
                 'lastModifiedTime': '1586513017763',
                 'routineReference': {'datasetId': 'dataset_1',
                                      'projectId': 'cm-da-mikami-yuki-258308',
                                      'routineId': 'test_function'},
                 'routineType': 'SCALAR_FUNCTION'}}
-------->
{'_properties': {'arguments': [{'dataType': {'typeKind': 'INT64'},
                                'mode': 'IN',
                                'name': 'id'},
                               {'dataType': {'typeKind': 'STRING'},
                                'mode': 'IN',
                                'name': 'value'}],
                 'creationTime': '1586506722471',
                 'definitionBody': 'BEGIN\n'
                                   '  INSERT INTO '
                                   'dataset_1.table_with_description\n'
                                   '  VALUES (id, value);\n'
                                   'END',
                 'etag': '6b9wxxxx2w==',
                 'language': 'SQL',
                 'lastModifiedTime': '1586506722471',
                 'routineReference': {'datasetId': 'dataset_1',
                                      'projectId': 'cm-da-mikami-yuki-258308',
                                      'routineId': 'test_procedure'},
                 'routineType': 'PROCEDURE'}}
-------->
{'_properties': {'creationTime': '1586513263155',
                 'definitionBody': 'SELECT 1;',
                 'description': 'プロシージャの説明',
                 'etag': 'bR4RxxxxBw==',
                 'language': 'SQL',
                 'lastModifiedTime': '1586513263155',
                 'routineReference': {'datasetId': 'dataset_1',
                                      'projectId': 'cm-da-mikami-yuki-258308',
                                      'routineId': 'test_procedure_description'},
                 'routineType': 'PROCEDURE'}}
-------->
{'_properties': {'arguments': [{'dataType': {'typeKind': 'FLOAT64'},
                                'name': 'x'}],
                 'creationTime': '1586508765922',
                 'definitionBody': '\nlib = "sample.js"\nreturn x*2;\n',
                 'etag': 'Lrvjxxxx6A==',
                 'language': 'JAVASCRIPT',
                 'lastModifiedTime': '1586508765922',
                 'returnType': {'typeKind': 'FLOAT64'},
                 'routineReference': {'datasetId': 'dataset_1',
                                      'projectId': 'cm-da-mikami-yuki-258308',
                                      'routineId': 'timesTwo'},
                 'routineType': 'SCALAR_FUNCTION'}}

※一部、伏字に変更しています。

list_routines でも get_routine でも、戻り値としては同じ google.cloud.bigquery.routine.Routine 型のオブジェクトが返却されますが、list_routines では NULL 値となっていた description などの以下のプロパティが追加で取得できました。

  • description
  • パラメータ情報
  • 戻り値のデータ型
  • ルーティン定義本文

アクセス制限されているリソース情報の取得

アクセスが許可されていないリソースの情報は取得できません。

サービスアカウントを切り替えてリソース情報を取得する、以下の Python を準備しました。

from google.cloud import bigquery
from pprint import pprint
from google.oauth2 import service_account
import argparse
import os.path

parser = argparse.ArgumentParser(description='select data')
parser.add_argument('file', help='account key file')
args = parser.parse_args()

key_path = os.path.join(os.path.dirname(os.path.abspath(__file__)), args.file)
credentials = service_account.Credentials.from_service_account_file(
    key_path,
    scopes=["https://www.googleapis.com/auth/cloud-platform"],
)
client = bigquery.Client(
    credentials=credentials,
    project=credentials.project_id,
)
#client = bigquery.Client()

project_id = 'cm-da-mikami-yuki-258308'
datasets = client.list_datasets(project_id)
if datasets:
    for obj in datasets:
        print('-------->')
        pprint(vars(obj))
else:
    print("This project does not contain any datasets.")

全てのデータセットにアクセス可能なサービスアカウントを指定して実行すると、以下の通り、5つ全てのデータセット情報が取得できます。

(test_bq) [ec2-user@ip-10-0-43-239 get_info]$ python get_dataset_by_account.py key_account_a.json
-------->
{'_properties': {'datasetReference': {'datasetId': 'airflow_test',
                                      'projectId': 'cm-da-mikami-yuki-258308'},
                 'id': 'cm-da-mikami-yuki-258308:airflow_test',
                 'kind': 'bigquery#dataset',
                 'location': 'US'}}
-------->
{'_properties': {'datasetReference': {'datasetId': 'billing',
                                      'projectId': 'cm-da-mikami-yuki-258308'},
                 'id': 'cm-da-mikami-yuki-258308:billing',
                 'kind': 'bigquery#dataset',
                 'location': 'asia-northeast1'}}
-------->
{'_properties': {'datasetReference': {'datasetId': 'dataset_1',
                                      'projectId': 'cm-da-mikami-yuki-258308'},
                 'id': 'cm-da-mikami-yuki-258308:dataset_1',
                 'kind': 'bigquery#dataset',
                 'labels': {'env': 'test'},
                 'location': 'asia-northeast1'}}
-------->
{'_properties': {'datasetReference': {'datasetId': 'dataset_2',
                                      'projectId': 'cm-da-mikami-yuki-258308'},
                 'id': 'cm-da-mikami-yuki-258308:dataset_2',
                 'kind': 'bigquery#dataset',
                 'location': 'asia-northeast1'}}
-------->
{'_properties': {'datasetReference': {'datasetId': 'test_dataset_option_all',
                                      'projectId': 'cm-da-mikami-yuki-258308'},
                 'friendlyName': 'オプション確認用',
                 'id': 'cm-da-mikami-yuki-258308:test_dataset_option_all',
                 'kind': 'bigquery#dataset',
                 'labels': {'option': 'all'},
                 'location': 'asia-northeast1'}}

dataset_1 へのアクセスしか許可されていないサービスアカウントに切り替えて実行してみると、該当アカウントでアクセス可能なデータセットの情報しか取得できませんでした。

(test_bq) [ec2-user@ip-10-0-43-239 get_info]$ python get_dataset_by_account.py key_account_c.json
-------->
{'_properties': {'datasetReference': {'datasetId': 'dataset_1',
                                      'projectId': 'cm-da-mikami-yuki-258308'},
                 'id': 'cm-da-mikami-yuki-258308:dataset_1',
                 'kind': 'bigquery#dataset',
                 'labels': {'env': 'test'},
                 'location': 'asia-northeast1'}}

同様に、アクセス許可されていないデータセット内のテーブル情報を取得しようとしてみると

(test_bq) [ec2-user@ip-10-0-43-239 get_info]$ python get_table_by_account.py key_account_c.json
Traceback (most recent call last):
  File "get_table_by_account.py", line 23, in <module>
    dataset = client.get_dataset(dataset_id)
  File "/home/ec2-user/test_bq/lib/python3.7/site-packages/google/cloud/bigquery/client.py", line 586, in get_dataset
    retry, method="GET", path=dataset_ref.path, timeout=timeout
  File "/home/ec2-user/test_bq/lib/python3.7/site-packages/google/cloud/bigquery/client.py", line 556, in _call_api
    return call()
  File "/home/ec2-user/test_bq/lib/python3.7/site-packages/google/api_core/retry.py", line 286, in retry_wrapped_func
    on_error=on_error,
  File "/home/ec2-user/test_bq/lib/python3.7/site-packages/google/api_core/retry.py", line 184, in retry_target
    return target()
  File "/home/ec2-user/test_bq/lib/python3.7/site-packages/google/cloud/_http.py", line 423, in api_request
    raise exceptions.from_http_response(response)
google.api_core.exceptions.Forbidden: 403 GET https://bigquery.googleapis.com/bigquery/v2/projects/cm-da-mikami-yuki-258308/datasets/dataset_2: Access Denied: Dataset cm-da-mikami-yuki-258308:dataset_2: User does not have bigquery.datasets.get permission for dataset cm-da-mikami-yuki-258308:dataset_2.

パーミッションエラーとなり、テーブル情報も参照できませんでした。

INFORMATION_SCHEMA から取得できる情報と比較

以下、INFORMATION_SCHEMA から取得できる情報と、クライアントライブラリから取得できる情報の一覧です。

リソース 項目 INFORMATION_SCHEMA Python Client Library
データセット プロジェクト ID
データセット ID (データセット名)
作成日時
最終更新日時
ロケーション
デフォルトのテーブルの有効期限
description
friendly name
ラベル情報
リソース種別( bigquery#dataset ) ×
アクセス制御情報 ×
API リクエスト URL ×
ETag 値 ×
テーブル / ビュー / カラム プロジェクト ID
データセット ID (データセット名)
テーブル ID (テーブル名)
テーブル種別
INSERT 文が実行可能かどうか ×
作成日時
有効期限
最終更新日時 ×
パーティションの有効期限
description
friendly name
ラベル情報
KMS キー
外部データソース ×
パーティションフィルタ要否
パーティション範囲情報 ×
分割タイプ ×
データサイズ( byte ) ×(※1)
行数 ×(※1)
ロケーション ×
リソース種別( bigquery#table ) ×
アクセス制御情報 ×
API リクエスト URL ×
ETag 値 ×
ビュー定義
ビューがレガシー SQL で作成されたかどうか
カラム名 〇(※2)
カラム順 ×
データ型 〇(※2)
NULLABLE かどうか 〇(※2)
疑似列かどうか ×
パーティショニングカラムかどうか ×
クラスタリングカラム順 ×
カラムの description 〇(※2)
カラムパス 〇(※2)
ルーティン プロジェクト ID
データセット ID (データセット名)
ルーティン ID (ルーティン名)
ルーティン種別
パラメータ情報
パラメータの順序 ×
戻り値のデータ型
UDF 定義言語
ルーティン定義本文
決定的関数かどうか ×
作成日時
最終更新日時
description
外部ライブラリ
ETag 値 ×

※1:メタテーブル( __TABLES__ )から取得可能(メタテーブルに対するクエリ課金はなし)

※2:JSON 形式のテーブルスキーマとして取得可能

まとめ(所感)

Python クライアントライブラリでも、INFORMATION_SCHEMA とほぼ同等の情報が取得できました。

現状、クライアントライブラリの方が取得できる項目は少し多いようですが、2020/04 現在 INFORMATION_SCHEMA はまだベータ版なので、 INFORMATION_SCHEMA でも今後さらに取得可能な項目が増える可能性もありそうです。

課金の面からみても現時点ではクライアントライブラリの方に分がある印象を受けましたが、クライアントライブラリでテーブルの詳細情報を取得する場合はテーブル数分の API コールが発生することになるので、処理時間が若干心配です。 要件に合わせて適切な I/F を選択する必要がありそうです。

参考