なぜEXCLUDEとRENAMEが必要なのか?
Snowflakeは2022年11月のリリースで、EXCLUDEとRENAMEという新しいSQL構文をひっそりと追加しました。これにより、SELECT 形式のクエリで特定の列を除外したりリネームしたりできるようになります。本番外のSQLワークフローでは、以下のような `SELECT ` の探索的クエリを実行する場面が非常に多いため、これは特に嬉しいアップデートです。
select *
from table
where id = 5
特定のレコードや一部のレコードを調べるためにこうしたクエリがよく使われます。しかし、特定の列(たとえばサイズの大きいテキスト列)を除外したい、あるいは列名を変えたい場合はどうでしょうか。これまでは、必要な列をすべて書き出した上で、リネームしたい列には従来通りAS構文を使うしかありませんでした。
select
column_1,
column_2,
column_3 as column_3_renamed,
column_4,
column_5,
column_6,
column_7,
column_8,
column_9, -- columns 10 and 11 are intentionally left out
column_12
from table
where id = 5
素早く答えを得たいときには、なかなか面倒です。
SnowflakeのSELECT \*文で列を除外する方法
列を一つひとつ書き出す代わりに、テーブルから除外したい列だけを指定したい——これはユーザーからよく寄せられる要望の一つです¹。多くの場合、含めたい列より除外したい列の方がはるかに少ないため、こちらの書き方の方がずっと効率的です。Snowflakeのバージョン6.37以降、SELECT *形式のクエリにEXCLUDE句を加えて、テーブルから取得したくない列を1つ以上指定できるようになりました。
select
*
exclude(column_10, column_11)
from table
where id = 5
単一の列を除外することもできます。
select
*
exclude (column_10)
from table
where id = 5
単一列の場合、括弧は省略可能です。上記のSQLは次のようにも書けます。
select
*
exclude column_10
from table
where id = 5
SnowflakeのEXCLUDEを複数テーブルで使う
列を除外したくなるもう一つの典型例が、複数テーブルを結合するケースです。次のようなクエリを思い浮かべてください²。
select
orders.*,
customers.* exclude customer_id,
items.* exclude order_id
from orders
join customers
on orders.customer_id=customers.customer_id
join items
on orders.order_id=items.order_id
この例では、order_idとcustomer_idが複数のテーブルに存在するため、出力にそれぞれ2つずつ含まれてしまいます。SQLを次のように書き換えれば、これらを簡単に除外できます。
select
orders.*,
customers.* exclude customer_id,
items.* exclude order_id
from orders
join customers
on orders.customer_id=customers.customer_id
join items
on orders.order_id=items.order_id
BigQueryやDatabricksのEXCEPTに相当するSnowflakeの構文
SnowflakeのEXCLUDE構文は、BigQueryやDatabricksのEXCEPT構文に似ています。
-- Snowflake
select
*
exclude(column_10, column_11)
from table
-- BigQuery/Databricks
select
*
except(column_10, column_11)
from table
DuckDBに馴染みがある方ならお気づきの通り、SnowflakeはDuckDBと同じEXCLUDE構文を採用しています。両者ともこの機能にEXCEPTキーワードを使わなかったのは、EXCEPTがすでに集合演算で使われているためと考えられます。
本番のSQLコードでEXCLUDEを使うべきか?
この新しい構文はアドホックなワークフローを効率化するうえでは非常に便利ですが、本番のSQLコードでの使用はおすすめしません。本番では、選択する列を明示的に書き出す方が安全です。すべての列名を書き出すことで可読性や監査性が高まり、コードを読む人はどのテーブルからどの列を使っているのかを一目で把握できます。エラーの内容も明確になります。テーブルから列が突然削除された場合、SQLコードはその時点で失敗してくれます。これは、欠落した列のせいで下流のアプリケーションが予期せずエラーになるよりも、はるかに望ましい挙動です。
SnowflakeのEXCLUDEはクエリプランやパフォーマンスに影響するか?
いいえ、影響しません。EXCLUDEキーワードで一部の列を除外しても、すべての列を明示的に書き出した場合とクエリプランやパフォーマンスは変わりません。実際にSnowflakeアカウント上のデータセットで両方のクエリを実行し、それぞれのクエリプロファイルを比較すれば確認できます。
SnowflakeのSELECT \*文で列をリネームする方法
これまではSELECT *クエリで列を一つでもリネームしたい場合、すべての列名を書き出す必要がありました。新しいRENAME機能を使えば、全列を選択しつつ、必要な列だけを手軽にリネームできます。
select
*
rename (column_3 as column_3_renamed, column_5 as column_5_renamed)
from table
where id = 5
1〜2列をリネームするためだけに全フィールドを列挙するより、ずっとスマートです。
-- old method 👎
select
column_1,
column_2,
column_3 as column_3_renamed,
column_4,
column_5 as column_5_renamed,
column_6,
...
column_12
from table
where id = 5
EXCLUDEと同様、単一列に対しても使え、括弧は省略可能です。
select
*
rename (column_3 as column_3_renamed)
from table
where id = 5
EXCLUDEとRENAMEを組み合わせる
EXCLUDEとRENAMEは、次のように組み合わせて使うこともできます。
select
*
exclude(column_10, column_11)
rename column_3 as column_3_renamed
from table
where id = 5
記事冒頭のコードと比べると、見違えるほどすっきりします。
select
column_1,
column_2,
column_3 as column_3_renamed,
column_4,
column_5,
column_6,
column_7,
column_8,
column_9, -- columns 10 and 11 are intentionally left out
column_12
from table
where id = 5
注釈
需要の大きさを示す一例として、約14年前に投稿されたこのStack Overflowの質問は、130万回以上閲覧され、1,000を超える賛成票を集めています。
この例のきっかけをくれたNate Sooterさんの最近のツイートに感謝します!
Ian Whitestone・Co-founder & CEO of SELECT
Ianは、Snowflakeのコスト管理・最適化を提供するSaaSプラットフォームSELECTのCo-founder兼CEOです。SELECTを立ち上げる以前は、ShopifyとCapital Oneでフルスタックのデータサイエンス・エンジニアリングチームを6年間率いていました。Shopifyでは、データウェアハウスの最適化とコスト可視性の向上を主導しました。