BigQueryでワイルドカードが使えるテーブルってどうやって作るの？

結論としてはしょうもない話なのだが、正解に辿り着くまでにまぁまぁ時間を要したので、
同じ問題に直面した人向けに記事化しておく

# 3行で

- 記事タイトルの疑問は、私がひたすらググり続けてもよく分からなかった疑問
- どんなにググっても解決されないのは当然で、別に特殊な作成方法など存在しないことが原因だった
- つまりBigQueryの`_TABLE_SUFFIX`は日付に限らずどんなパターンでもワイルドカードで取得できる

# そもそもなぜ私はワイルドカードで取得するタイプのテーブルには特別なテーブル作成が必要だと思っていたのか？

[BigQueryの公式リファレンス](https://cloud.google.com/bigquery/docs/querying-wildcard-tables?hl=ja)にはワイルドカード（*）を利用したテーブルのクエリ方法についての説明はすでに存在している。

クエリの走査ボリュームを減らすためにもこの機能は可能な限り活用したいのでどうやったらワイルドカードで引っかかるようにサフィックス付きのテーブルを作成出来るのか調べることに。。。。

そして、どんなにググってもワイルドカードを利用して`_TABLE_SUFFIX`をWHERE句に指定して絞っているクエリ例しかなく発見出来ずやや途方にくれていた。


## WebUIではどう表現されているか

BigQueryのパブリックデータ`ga_sessions`は日付別にテーブルが管理されており、こんな感じを再現したい
`bigquery-public-data.google_analytics_sample.ga_sessions_` 

実際に、BigQueryをWebUIで確認すると複数のテーブルがこんな感じで格納されている。

![スクリーンショット 2019-10-12 9.33.06.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/55225/6a7fb488-93d0-ae6a-c9e6-96abec901f52.png)

WebUIで見ると、366日分のテーブルに関して日付を認識しつつクエリできるように設計されているように見える。。。

青枠で囲っている部分が日付で選択できるので、私はてっきりこの手のワイルドカードを使うテーブルは日付を認識できるように特別な方法でCreate Tableしなければ使えないと思い込んでいた。

ただ、これは完全なる勘違いで、WebUI上でそう表現されているだけでテーブル名は日付で保持していたりはしなかった。

# 結局どうすればいいのか？
結論から言えば

**自分が認識しやすいサフィックスをテーブル名に付けておけばどんなテーブル名でもワイルドカードで取得できる**

ただ、[BiqQueryのテーブル名の規則](https://cloud.google.com/bigquery/docs/tables?hl=ja#table_naming)では
> 1,024 文字以内
> 英字（大文字または小文字）、数字、アンダースコアだけが含まれている

となっているため日付のフォーマットでテーブル名を作成することは不可。

何を言っているかと言うと下記のケースのことを言っている↓

`sales_actual_data_2019-01-02`みたいなテーブルは作成出来ず、
`sales_actual_data_20190102`の命名形式でテーブルを作成する必要がある


# 実際にテーブルを作ってみて挙動を確かめる
試しに２つのパターンでテーブルを作ってみてワイルドカードでクエリできるかを試す

## 1. サフィックスに日付っぽい数値を入れて日付として認識されるか試す
DataSet名:`demo`でテーブル名:`demo_table_{Date}`となるようなテーブルを作成してWebUI上どのように見えるか実際に確認する

まずは、2019/01/01の1日分のテーブルだけを作成した場合

```python
from google.cloud.bigquery import Client, SchemaField, Table

project_id = '{YourGCPProjectID}'
client = Client(project=project_id)

# DataSet名
dataset_name = "demo"

# １つだけテーブルを作成する
table_name = "demo_table_20190101"
table_id = "{}.{}.{}".format(client.project, dataset_name, table_name)

# DDLのようにテーブルの定義をSchemaとして記載
schema = [
 SchemaField('ItemId', 'INTEGER',  'NULLABLE', None),
 SchemaField('Number', 'INTEGER',  'NULLABLE', None),
 SchemaField('Amount', 'INTEGER',  'NULLABLE', None),
 SchemaField('Date',   'DATETIME', 'NULLABLE', None),
]

# テーブルを作成
table = Table(table_id, schema=schema)
table = client.create_table(table) 
```

画面上はこんな感じで反映される
![スクリーンショット 2019-10-13 9.58.17.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/55225/bc0c360f-0ccb-95ef-800b-bec9840cd14c.png)

どうやらこの時点では、このサフィックスを日付だとは認識していない模様

次に2019/01/02-2019/01/05の４日分を追加でテーブル作成してみる

```python

# 試しに2019/01/02/-2019/01/05のテーブルを追加で作成してみる
for table_suffix in range(20190102,20190106):
    table_name =  "demo_table_{}".format(table_suffix)
    table_id = "{}.{}.{}".format(client.project, dataset_name, table_name)
    table = Table(table_id, schema=schema)
    table = client.create_table(table)     
```

すると画面ではサフィックス部分がまとめられてこんな感じで表示されるようになる

![スクリーンショット 2019-10-13 10.02.50.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/55225/78dc711c-c591-181b-e0f4-97c8d0f83f9f.png)

そして、日付部分がドロップダウンリスト形式で選べるようになっている

もちろん、このテーブルをワイルドカードでクエリできる

```sql:ワイルドカードを用いたクエリ
SELECT 
    ItemId
  , Number
  , Amount
  , Date
FROM 
    `{YourGCPProjectID}.demo.demo_table_*` 
WHERE
    _TABLE_SUFFIX BETWEEN '20190101' AND '20190103'
LIMIT 1000
;
```

## 2. 文字列をサフィックスにしてワイルドカードが機能するか試す
日付はいい感じで出来そうなのはわかったので、次にサフィックスが文字列でも機能するかを試す

適当に球技名をサフィックスに付けてテーブルがどのように認識されるか試す

```python:文字列をサフィックスにする
# DDLのようにテーブルの定義をSchemaとして記載
schema = [
 SchemaField('ItemId', 'INTEGER', 'NULLABLE', None),
 SchemaField('Number', 'INTEGER', 'NULLABLE', None),
 SchemaField('Amount', 'INTEGER', 'NULLABLE', None),
 SchemaField('Genre', 'STRING', 'NULLABLE', None),
]

# 商品やジャンル別にテーブルが別れていたと仮定して複数テーブルを作成してみる
for table_suffix in ["football","baseball","rugbyball","volleyball"] :
    table_name =  "item_table_{}".format(table_suffix)
    table_id = "{}.{}.{}".format(client.project, dataset_name, table_name)
    table = Table(table_id, schema=schema)
    table = client.create_table(table)
```

画面上は、日付のようにまとまっては表示されない

![スクリーンショット 2019-10-13 10.42.50.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/55225/39b56e38-aa14-77d7-5e5c-cea7d0ca3ae7.png)

しかし、ワイルドカード自体は有効で、下記のようにクエリが可能なことは確認した

```sql:文字列に対してワイルドカードを使ったクエリ
SELECT 
       ItemId
     , Number
     , Amount
     , Genre
FROM 
    `{YourGCPProjectID}.demo.item_table_*` 
WHERE
    _TABLE_SUFFIX IN ('baseball', 'football' )
LIMIT 100
;
```

おしまい

# 参考リンク
[ワイルドカード テーブルを使用した複数テーブルに対するクエリ](https://cloud.google.com/bigquery/docs/querying-wildcard-tables?hl=ja)

[５分でわかる！BigQuery Tips集](https://data.gunosy.io/entry/2016/10/06/092502)