Snowflake の SQL でパイプ演算子が使えるようになりました

Snowflake の SQL でパイプ演算子が使えるようになりました

Clock Icon2025.05.22

はじめに

2025年5月のリリースで新しいパイプ演算子 ( ->>) を使用して SQL 文を連結できるようになりました。こちらを試してみましたので、本記事で内容をまとめてみます。

https://docs.snowflake.com/en/release-notes/2025/9_13#pipe-operator

機能概要

本機能については以下に記載があります。

https://docs.snowflake.com/en/sql-reference/operators-flow

Snowflake におけるパイプ演算子(->>)は、複数の SQL ステートメントを連結し、前のクエリ結果を次のクエリで簡単に参照できる構文です。
これにより、複雑な処理をステップごとにわかりやすく記述できるようになります。例えば、データ分析における前処理や加工処理の場面で、一連の処理の流れを明示的に記述したい場合に有効です。

Snowflake では以下のように記述します。

first_st ->> secound_st

これによりパイプ以降の処理(secound_st)では first_st の処理結果を参照することができるようになります。この際、ドル記号($)とパイプ番号(現在の文から逆算したチェーン内の文の相対位置)を含むパラメータを FROM 句で使用することで前の処理結果を参照できます。

上記の場合、以下のように記述でき、最後の SQL 文が最終結果として出力されます。

SELECT * FROM <table>
    ->> SELECT * FROM $1 WHERE col = 'val';

ドキュメントによるとパイプ番号($1, $2 など)は「現在の文から逆算したチェーン内の文の相対位置」と記載があります。
つまりパイプ番号は、現在の SQL 文から見て、どの位置にある前のステートメントかを示します。$1は直前、$2 は2つ前、というように、現在の SQL 文を基準に後ろから前に向かって順番に番号が付けられます。

以下の公式ドキュメントの例では、それぞれのステートメントがどのように$nで参照されているかが記載されています。

first_st -- last_st では $4、fourth_st では $3、third_st では $2、second_st では $1 として参照される
  ->> second_st -- last_st では $3、fourth_st では $2、third_st では $1 として参照される
  ->> third_st  -- last_st では $2、fourth_st では $1 として参照される
  ->> fourth_st -- last_st では $1 として参照される
  ->> last_st;

参照される処理結果は、参照先の SQL ステートメントのクエリ ID が渡された RESULT_SCAN と同等の結果が返ります。公式ドキュメントの引用ですが以下は同じ結果を返します。

-- RESULT_SCAN で直前のクエリ結果を参照
SHOW WAREHOUSES;
SELECT "name", "state", "type", "size"
  FROM TABLE(RESULT_SCAN(LAST_QUERY_ID(-1)));

--パイプ演算子で SHOW コマンドの結果を参照  
SHOW WAREHOUSES
  ->> SELECT "name", "state", "type", "size" FROM $1;

RESULT_SCAN との違いとして、パイプ演算子で連結された処理は順序どおりに実行され、同時実行される外部クエリの影響を受けることなく、チェーン内の正しい結果を参照することができます。

試してみる

パイプ演算子は SHOW、SELECT、CREATE、INSERTなど、有効なSQL文を任意に指定できるようです。
以降はいくつかクエリしてみた例を記載します。

SHOW

SHOW PARAMETERS IN SESSION
    ->> SELECT "key","value","default" FROM $1 WHERE "key" IN ('LOG_LEVEL','TRACE_LEVEL');
+-------------+-------+---------+
| key         | value | default |
|-------------+-------+---------|
| LOG_LEVEL   | OFF   | OFF     |
| TRACE_LEVEL | OFF   | OFF     |
+-------------+-------+---------+

DESCRIBE

--従来のクエリ例
DESCRIBE table mytable;
SELECT "name","type" FROM TABLE(result_scan(-1));

--パイプ演算子
DESCRIBE table mytable
	->> SELECT "name","type" FROM $1;
+----------+-------------------+
| name     | type              |
|----------+-------------------|
| ID       | NUMBER(2,0)       |
| VAL      | NUMBER(10,9)      |
| CATEGORY | VARCHAR(16777216) |
| DATE     | DATE              |
+----------+-------------------+

LIST

--従来のクエリ例
ls @s3_stage;
SELECT "name","last_modified" FROM TABLE(result_scan(-1));

--パイプ演算子
ls @s3_stage
    ->> SELECT "name","last_modified" FROM $1;
+---------------------------------------------------+-------------------------------+
| name                                              | last_modified                 |
|-----------------------------------------------------------------+-----------------|
| s3://<bucket>/2024/04/12/sample_data_20240412.csv | Tue, 20 May 2025 10:35:09 GMT |
| s3://<bucket>/2024/04/13/sample_data_20240413.csv | Tue, 20 May 2025 10:35:20 GMT |
| s3://<bucket>/2024/04/14/sample_data_20240414.csv | Tue, 20 May 2025 10:35:30 GMT |
| s3://<bucket>/2024/04/15/sample_data_20240415.csv | Wed, 21 May 2025 09:31:15 GMT |
+---------------------------------------------------+-------------------------------+

表形式の結果を返すストアドプロシージャ

--従来のクエリ例
CALL get_regions(['AMERICA','ASIA']);
SELECT r_name,r_comment FROM TABLE(get_regions(['AMERICA','ASIA']));

--パイプ演算子
CALL get_regions(['AMERICA','ASIA'])
    ->> SELECT r_name,r_comment FROM $1;
 +---------+---------------------------------+
| R_NAME  | R_COMMENT                       |
|---------+---------------------------------|
| AMERICA | hs use ironic, even requests. s |
| ASIA    | ges. thinly even pinto beans ca |
+---------+---------------------------------+

時系列予測との組み合わせ

CALL model1!FORECAST(FORECASTING_PERIODS => 3)
    ->> CREATE OR REPLACE TABLE my_forecasts AS SELECT * FROM $1
    ->> SELECT * FROM $2; 
+--------+-------------------------+--------------+--------------+--------------+
| SERIES | TS                      |     FORECAST |  LOWER_BOUND |  UPPER_BOUND |
|--------+-------------------------+--------------+--------------+--------------|
| NULL   | 2020-01-13 00:00:00.000 | 14.000000182 | 14.000000163 | 14.0000002   |
| NULL   | 2020-01-14 00:00:00.000 | 15.000000364 | 15.000000304 | 15.000000419 |
| NULL   | 2020-01-15 00:00:00.000 | 16.000000545 | 16.000000444 | 16.000000645 |
+--------+-------------------------+--------------+--------------+--------------+

データ加工

例として以下のテーブルに対して処理をしてみます。

>SELECT * FROM CUSTOMERS;
+-------------+------------+-----------+-----------------------------+-------------+
| CUSTOMER_ID | FIRST_NAME | LAST_NAME | EMAIL                       | SIGNUP_DATE |
|-------------+------------+-----------+-----------------------------+-------------|
|           1 | Taro       | Yamada    | taro.yamada@example.com     | 2023-01-15  |
|           2 | Hanako     | Suzuki    | hanako.suzuki@example.com   | 2023-02-20  |
|           3 | John       | Smith     | john.smith@example.com      | 2023-03-10  |
|           4 | Emma       | Tanaka    | emma.tanaka@example.com     | 2023-04-05  |
|           5 | Kenta      | Kobayashi | kenta.kobayashi@example.com | 2023-05-01  |
+-------------+------------+-----------+-----------------------------+-------------+

-- パイプ演算子で特定の顧客にフィルタし集計
SELECT * FROM CUSTOMERS
  ->> SELECT *, FIRST_NAME || ' ' || LAST_NAME AS FULL_NAME FROM $1 -- カラム追加:FULL_NAME を作成
  ->> SELECT * FROM $1 WHERE SIGNUP_DATE >= '2023-03-01' -- フィルタ:2023年3月以降に登録したユーザー
  ->> SELECT TO_CHAR(SIGNUP_DATE, 'YYYY-MM') AS SIGNUP_MONTH, COUNT(*) AS USER_COUNT FROM $1 GROUP BY 1 ORDER BY 1; -- 集計:月ごとのユーザー数

ここではシンプルな例ですが、パイプ演算子を連結させていくことで、各加工段階での処理内容を追いやすくなると思います。

データ処理時の注意点

以下はドキュメントに記載がある例ですが、パイプ演算子を使用することで、先の例のように一連の処理内容を完結に記述できます。

SELECT * FROM dept_pipe_demo WHERE dname = 'SALES'
  ->> SELECT * FROM emp_pipe_demo WHERE sal > 1500 AND deptno IN (SELECT deptno FROM $1)
  ->> SELECT ename, sal FROM $1 ORDER BY 2 DESC;

ただし、結合クエリでも同じ出力が得られ、通常は結合クエリの方がパイプ演算子を組み合わせたクエリよりもパフォーマンスが向上するようです。
そのため、SQL での探索的な分析時にパイプ演算子を使い、データ分析のパイプラインに組み込む際は結合クエリに置き換える、といった使い分けもできそうです。

SELECT 
	ename
	,sal
FROM 
	emp_pipe_demo
WHERE
	sal > 1500 AND deptno IN (
    SELECT deptno
    FROM dept_pipe_demo
    WHERE dname = 'SALES'
  )
ORDER BY sal DESC;

さいごに

SQL で使用できるパイプ演算子 ( ->>) を試してみました。
SHOW コマンドなどの操作がより直感的になるほか、特にデータ分析における前処理や加工の場面で、再現性や可読性の向上が期待できる便利な機能だと感じました。
これまでは R の tidyverse など、特定のプログラミング言語を通じて提供されていた機能が、SQL でも利用できるようになった点は非常に嬉しいアップデートと思います。
また、Snowflake では機械学習や大規模言語モデル(LLM)関連の機能も SQL で実行可能な仕組みが提供されているので、SQL ユーザーにとってさらに分析しやすい環境になってきていると感じました。
こちらの内容が何かの参考になれば幸いです。

Share this article

facebook logohatena logotwitter logo

© Classmethod, Inc. All rights reserved.