BigQuery Scriptingでcolumnのcardinalityを調べる
はじめに
BigQuery Scriptingで任意のtableのすべてのcolumnのcardinalityを調べる方法をご紹介します。このscriptでは、各columnに対して COUNT(DISTINCT)
を実行するqueryを動的に生成します。
cardinalityとは
Databaseにおけるcardinalityとは、あるtableのcolumnに存在するuniqueな値の数を指します。これはデータの分布や多様性を把握する上で重要な指標です。また、Relational Databaseではcardinalityが高い(unique値が多い)columnにindexを貼ることで検索パフォーマンスを向上させます。一方、BigQueryでは従来のRelational Databaseで使用されるindex(B-treeやHash indexなど)はありませんが、高いcardinalityのcolumnをfilter条件に利用することでqueryのパフォーマンスを向上させることができます。
個人的にBigQueryでcardinalityを調べるタイミングとしては、以下のような場面が挙げられます。
- 初めて扱うtableのデータ分布を把握したいとき。
- 複雑なqueryで作成されたdatamartの一意性が正しいか確認したいとき。
- 特定の識別子やID列が一意であるかを検証したいとき。
script
方法は、INFORMATION_SCHEMA
からcolumnを取得し、各columnについて COUNT(DISTINCT)
を実行し、その結果を UNION ALL
で結合するqueryを組み立てます。
DECLARE project_id STRING DEFAULT 'project_id';
DECLARE dataset_id STRING DEFAULT 'dataset_id';
DECLARE table_id STRING DEFAULT 'table_id';
DECLARE cardinality_query STRING;
SET cardinality_query = '';
FOR column_names IN (
SELECT column_name
FROM `project_id.dataset_id.INFORMATION_SCHEMA.COLUMNS`
WHERE table_name = 'table_id'
)
DO
IF cardinality_query = '' THEN
SET cardinality_query =
FORMAT(
"SELECT '%s' AS column, COUNT(DISTINCT `%s`) AS cardinality FROM `%s.%s.%s`",
column_names.column_name,
column_names.column_name,
project_id,
dataset_id,
table_id
);
ELSE
SET cardinality_query =
FORMAT(
"%s UNION ALL SELECT '%s' AS column, COUNT(DISTINCT `%s`) AS cardinality FROM `%s.%s.%s`",
cardinality_query,
column_names.column_name,
column_names.column_name,
project_id,
dataset_id,
table_id
);
END IF;
END FOR;
EXECUTE IMMEDIATE cardinality_query;
このscriptで以下のようなqueryが生成され実行されます。
SELECT
'id' AS column,
COUNT(DISTINCT `id`) AS cardinality
FROM
`project_id.dataset_id.table_id`
UNION ALL
SELECT
'name' AS column,
COUNT(DISTINCT `name`) AS cardinality
FROM
`project_id.dataset_id.table_id`
UNION ALL
SELECT
'age' AS column,
COUNT(DISTINCT `age`) AS cardinality
FROM
`project_id.dataset_id.table_id`
UNION ALL
SELECT
'sex' AS column,
COUNT(DISTINCT `sex`) AS cardinality
FROM
`project_id.dataset_id.table_id`
UNION ALL
SELECT
'email' AS column,
COUNT(DISTINCT `email`) AS cardinality
FROM
`project_id.dataset_id.table_id`
UNION ALL
SELECT
'address' AS column,
COUNT(DISTINCT `address`) AS cardinality
FROM
`project_id.dataset_id.table_id`
[
{ "column": "address", "cardinality": "100" },
{ "column": "email", "cardinality": "100" },
{ "column": "sex", "cardinality": "3" },
{ "column": "age", "cardinality": "41" },
{ "column": "name", "cardinality": "100" },
{ "column": "id", "cardinality": "100" }
]
table sizeが大きい場合や、高いcardinalityを持つcolumnに対して COUNT(DISTINCT)
を実行する場合は計算コストが増加します。その場合は APPROX_COUNT_DISTINCT
を使用することでパフォーマンスを犠牲にせずに概算値を取得できます。
Discussion