RESULTSETs の操作¶
このトピックでは、Snowflakeスクリプトで RESULTSET を使用する方法について説明します。
概要¶
Snowflakeスクリプトでは、 RESULTSET はクエリの結果セットをポイントする SQL データ型です。
RESULTSET は結果への単なるポインターであるため、 RESULTSET を介して結果にアクセスするには、次のいずれかを実行する必要があります。
TABLE(...)
構文を使用して、結果をテーブルとして取得します。カーソル を使用して RESULTSET を反復処理します。
これらの両方の例を以下に示します。
カーソルと RESULTSET の違いを理解する¶
RESULTSET と カーソル はどちらも、クエリの結果セットへのアクセスを提供します。ただし、これらのオブジェクトは次の点で異なります。
クエリが実行された時点。
カーソルの場合、カーソルに対して OPEN コマンドを実行すると、クエリが実行されます。
RESULTSET の場合は、クエリを RESULTSET に割り当てるとクエリが実行されます(DECLARE セクション内または BEGIN ... END ブロック内のいずれか)。
OPEN コマンドでのバインドのサポート。
カーソルを宣言するときに、バインドパラメーター(
?
文字)を指定できます。後で OPEN コマンドを実行すると、 USING 句で変数をそれらのパラメーターにバインドできます。RESULTSET は、 OPEN コマンドをサポートしていません。ただし、結果セットを返す前に、 SQL コマンドで変数をバインドできます。
一般に、クエリの結果セットを含むテーブルを返す場合は、 RESULTSET を使用する方が簡単です。ただし、カーソルを使用してSnowflakeスクリプトブロックからテーブルを返すこともできます。そのためには、カーソルを RESULTSET_FROM_CURSOR(cursor)
に渡して RESULTSET を返し、その RESULTSET を TABLE(...)
に渡します。 カーソルのテーブルを返す をご参照ください。
RESULTSET の宣言¶
ブロックの DECLARE セクションまたはブロックの BEGIN ... END セクションで RESULTSET を宣言できます。
DECLARE セクション内において、 RESULTSET 宣言構文 で説明されている構文を使用します。例:
DECLARE ... res RESULTSET DEFAULT (SELECT col1 FROM mytable ORDER BY col1);
BEGIN ... END ブロック内において、 RESULTSET 割り当て構文 で説明されている構文を使用します。例:
BEGIN ... LET res RESULTSET := (SELECT col1 FROM mytable ORDER BY col1);
宣言された RESULTSET へのクエリの割り当て¶
すでに宣言されている RESULTSET にクエリの結果を割り当てるには、次の構文を使用します。
<resultset_name> := [ ASYNC ] ( <query> ) ;
条件:
resultset_name
RESULTSET の名前。
名前は、現在のスコープ内で一意である必要があります。
名前は、 オブジェクト識別子 の名前付け規則に従う必要があります。
ASYNC
クエリを非同期の子ジョブとして実行します。
クエリには、 SELECT ステートメントや DML ステートメント(INSERT や UPDATE など)を含む、有効な SQL ステートメントを指定できます。
このキーワードを省略すると、ストアドプロシージャは子ジョブを順次実行し、各子ジョブは実行中の子ジョブの終了を待ってから開始します。
このキーワードを使用すると、複数の子ジョブを同時に実行できるため、効率が向上し、全体の実行時間が短縮されます。
query
RESULTSET に割り当てるクエリ。
クエリを RESULTSET に割り当てる方法:
DECLARE
res RESULTSET;
BEGIN
res := (SELECT col1 FROM mytable ORDER BY col1);
...
クエリを RESULTSET に割り当て、非同期の子ジョブとしてクエリを実行する方法:
DECLARE
res RESULTSET;
BEGIN
res := ASYNC (SELECT col1 FROM mytable ORDER BY col1);
...
クエリ用に SQL 文字列を動的に構築するには、 query
を (EXECUTE IMMEDIATE string_of_sql)
にセットします。例:
DECLARE
res RESULTSET;
col_name VARCHAR;
select_statement VARCHAR;
BEGIN
col_name := 'col1';
select_statement := 'SELECT ' || col_name || ' FROM mytable';
res := (EXECUTE IMMEDIATE :select_statement);
RETURN TABLE(res);
END;
RESULTSET に対して EXECUTE IMMEDIATE ステートメントに query
をセットすることはできますが、カーソルに対してこれを行うことはできません。
RESULTSET の使用¶
RESULTSET のクエリは、オブジェクトがそのクエリに関連付けられているときに実行されます。例:
RESULTSET を宣言し、 DEFAULT 句をクエリに設定すると、その時点でクエリが実行されます。
:=
演算子を使用してクエリを RESULTSET に割り当てると、その時点でクエリが実行されます。
注釈
RESULTSET はクエリの結果セットをポイントしているため(クエリの結果セットは含まれていません)、 RESULTSET はクエリ結果がキャッシュされている間(通常は24時間)のみ有効です。クエリ結果のキャッシュの詳細については、 保存済みのクエリ結果の使用 をご参照ください。
クエリが実行されると、カーソルを使用して結果にアクセスできます。ストアドプロシージャから結果をテーブルとして返すこともできます。
カーソルの使用による RESULTSET からのデータへのアクセス¶
カーソルを使用して RESULTSET からデータにアクセスするには、オブジェクト上で カーソルを宣言 します。例:
DECLARE
...
res RESULTSET DEFAULT (SELECT col1 FROM mytable ORDER BY col1);
c1 CURSOR FOR res;
RESULTSET でカーソルを宣言すると、カーソルはすでに RESULTSET にあるデータにアクセスできます。カーソルで OPEN コマンドを実行しても、 RESULTSET のクエリは再度実行されません。
次に、 カーソルを開き、カーソルを使用して データをフェッチ します。
注釈
結果に GEOGRAPHY 値が含まれる場合は、 GEOGRAPHY 入力値を期待する関数に値を渡す前に、値を GEOGRAPHY 型にキャストする必要があります。 カーソルの使用による GEOGRAPHY 値の取得 をご参照ください。
RESULTSET をテーブルとして返す¶
RESULTSET がポイントする結果を返す場合は、 RESULTSET を TABLE(...)
に渡します。例:
CREATE PROCEDURE f()
RETURNS TABLE(column_1 INTEGER, column_2 VARCHAR)
...
RETURN TABLE(my_resultset_1);
...
これは、 TABLE(...)
が テーブル関数 (RESULT_SCAN など)で使用される方法と似ています。
例に示すように、テーブルを返すストアドプロシージャを作成する場合は、テーブルを返すものとしてストアドプロシージャを宣言する必要があります。
注釈
現在、 TABLE(resultset_name)
構文は、 RETURN ステートメントでのみサポートされています。
カーソルを使用して RESULTSET から行をフェッチ した場合でも、 TABLE(resultset_name)
によって返されるテーブルには、(カーソルの内部行ポインターから始まる行だけでなく)すべての行が含まれています。
RESULTSET データ型の制限¶
RESULTSET はデータ型ですが、Snowflakeはまだ以下をサポートしていません。
型 RESULTSET の列を宣言する。
型 RESULTSET のパラメーターを宣言する。
ストアドプロシージャの戻り型を RESULTSET として宣言する。
Snowflakeは、Snowflakeスクリプト内でのみ RESULTSET をサポートします。
また、 RESULTSET を直接テーブルとして使用することはできません。たとえば、次は無効です。
SELECT * FROM my_result_set;
RESULTSET の使用例¶
次のセクションでは、 RESULTSET の使用例を示します。
例に対するデータの設定¶
以下の例の多くでは、以下に示すテーブルとデータを使用しています。
CREATE OR REPLACE TABLE t001 (a INTEGER, b VARCHAR);
INSERT INTO t001 (a, b) VALUES
(1, 'row1'),
(2, 'row2');
例: ストアドプロシージャからテーブルを返す¶
次のコードは、 RESULTSET を宣言し、 RESULTSET がポイントする結果を返す方法を示しています。CREATE PROCEDURE コマンドの RETURNS 句は、ストアドプロシージャが INTEGER 型の1つの列を含むテーブルを返すことを宣言しています。
ブロック内の RETURN ステートメントは、 TABLE(...)
構文を使用して結果をテーブルとして返します。
ストアドプロシージャを作成します。
CREATE OR REPLACE PROCEDURE test_sp()
RETURNS TABLE(a INTEGER)
LANGUAGE SQL
AS
DECLARE
res RESULTSET DEFAULT (SELECT a FROM t001 ORDER BY a);
BEGIN
RETURN TABLE(res);
END;
注: SnowSQL、 Classic Console、 execute_stream
または execute_string
メソッドを Python Connector コードで使用している場合は、代わりにこの例を使用してください(SnowSQL、 Classic Console、Python ConnectorでSnowflakeスクリプトを使用する を参照)。
CREATE OR REPLACE PROCEDURE test_sp()
RETURNS TABLE(a INTEGER)
LANGUAGE SQL
AS
$$
DECLARE
res RESULTSET default (SELECT a FROM t001 ORDER BY a);
BEGIN
RETURN TABLE(res);
END;
$$;
ストアドプロシージャを呼び出します。
CALL test_sp();
+---+
| A |
|---|
| 1 |
| 2 |
+---+
RESULT_SCAN 関数を使用して、ストアドプロシージャ呼び出しの結果を処理することもできます。
SELECT *
FROM TABLE(RESULT_SCAN(LAST_QUERY_ID()))
ORDER BY 1;
+---+
| A |
|---|
| 1 |
| 2 |
+---+
例: SQL ステートメントを動的に作成する¶
SQL を動的に構築できます。以下は、前のストアドプロシージャと同じクエリを実行しますが、動的に構築される SQL ステートメントを使用する例です。
CREATE OR REPLACE PROCEDURE test_sp_dynamic(table_name VARCHAR)
RETURNS TABLE(a INTEGER)
LANGUAGE SQL
AS
DECLARE
res RESULTSET;
query VARCHAR DEFAULT 'SELECT a FROM IDENTIFIER(?) ORDER BY a;';
BEGIN
res := (EXECUTE IMMEDIATE :query USING(table_name));
RETURN TABLE(res);
END;
注: SnowSQL、 Classic Console、 execute_stream
または execute_string
メソッドを Python Connector コードで使用している場合は、代わりにこの例を使用してください(SnowSQL、 Classic Console、Python ConnectorでSnowflakeスクリプトを使用する を参照)。
CREATE OR REPLACE PROCEDURE test_sp_dynamic(table_name VARCHAR)
RETURNS TABLE(a INTEGER)
LANGUAGE SQL
AS
$$
DECLARE
res RESULTSET;
query VARCHAR DEFAULT 'SELECT a FROM IDENTIFIER(?) ORDER BY a;';
BEGIN
res := (EXECUTE IMMEDIATE :query USING(table_name));
RETURN TABLE(res);
END
$$
;
例を実行するには、ストアドプロシージャを呼び出し、テーブル名を渡します。
CALL test_sp_dynamic('t001');
+---+
| A |
|---|
| 1 |
| 2 |
+---+
例: DEFAULT 句なしで RESULTSET 変数を宣言する¶
次のコードは、 DEFAULT 句なしで(つまり、クエリを RESULTSET に関連付けずに) RESULTSET を宣言し、後で RESULTSET をクエリに関連付ける方法を示しています。
CREATE OR REPLACE PROCEDURE test_sp_02()
RETURNS TABLE(a INTEGER)
LANGUAGE SQL
AS
DECLARE
res RESULTSET;
BEGIN
res := (SELECT a FROM t001 ORDER BY a);
RETURN TABLE(res);
END;
注: SnowSQL、 Classic Console、 execute_stream
または execute_string
メソッドを Python Connector コードで使用している場合は、代わりにこの例を使用してください(SnowSQL、 Classic Console、Python ConnectorでSnowflakeスクリプトを使用する を参照)。
CREATE OR REPLACE PROCEDURE test_sp_02()
RETURNS TABLE(a INTEGER)
LANGUAGE SQL
AS
$$
DECLARE
res RESULTSET;
BEGIN
res := (SELECT a FROM t001 ORDER BY a);
RETURN TABLE(res);
END;
$$;
例を実行するには、ストアドプロシージャを呼び出します。
CALL test_sp_02();
+---+
| A |
|---|
| 1 |
| 2 |
+---+
例: CURSOR を RESULTSET で使用する¶
次のコードは、 カーソル を使用して RESULTSET の行を反復処理する方法を示しています。
ストアドプロシージャを作成します。
CREATE OR REPLACE PROCEDURE test_sp_03()
RETURNS VARCHAR
LANGUAGE SQL
AS
DECLARE
accumulator INTEGER DEFAULT 0;
res1 RESULTSET DEFAULT (SELECT a FROM t001 ORDER BY a);
cur1 CURSOR FOR res1;
BEGIN
FOR row_variable IN cur1 DO
accumulator := accumulator + row_variable.a;
END FOR;
RETURN accumulator::VARCHAR;
END;
注: SnowSQL、 Classic Console、 execute_stream
または execute_string
メソッドを Python Connector コードで使用している場合は、代わりにこの例を使用してください(SnowSQL、 Classic Console、Python ConnectorでSnowflakeスクリプトを使用する を参照)。
CREATE OR REPLACE PROCEDURE test_sp_03()
RETURNS INTEGER
LANGUAGE SQL
AS
$$
DECLARE
accumulator INTEGER DEFAULT 0;
res1 RESULTSET DEFAULT (SELECT a FROM t001 ORDER BY a);
cur1 CURSOR FOR res1;
BEGIN
FOR row_variable IN cur1 DO
accumulator := accumulator + row_variable.a;
END FOR;
RETURN accumulator;
END;
$$;
ストアドプロシージャを呼び出すと、結果にテーブル内の a
の値が追加されます(1 + 2)。
CALL test_sp_03();
+------------+
| TEST_SP_03 |
|------------|
| 3 |
+------------+
例:テーブルを同時にクエリする子ジョブの実行¶
以下のコードは、 ASYNC キーワードを使用して、テーブルをクエリする複数の子ジョブを同時に実行する方法を示しています。
この例では、以下の表のデータを使用します。
CREATE OR REPLACE TABLE orders_q1_2024 (
order_id INT,
order_amount NUMBER(12,2));
INSERT INTO orders_q1_2024 VALUES (1, 500.00);
INSERT INTO orders_q1_2024 VALUES (2, 225.00);
INSERT INTO orders_q1_2024 VALUES (3, 725.00);
INSERT INTO orders_q1_2024 VALUES (4, 150.00);
INSERT INTO orders_q1_2024 VALUES (5, 900.00);
CREATE OR REPLACE TABLE orders_q2_2024 (
order_id INT,
order_amount NUMBER(12,2));
INSERT INTO orders_q2_2024 VALUES (1, 100.00);
INSERT INTO orders_q2_2024 VALUES (2, 645.00);
INSERT INTO orders_q2_2024 VALUES (3, 275.00);
INSERT INTO orders_q2_2024 VALUES (4, 800.00);
INSERT INTO orders_q2_2024 VALUES (5, 250.00);
以下のストアドプロシージャは、以下のアクションを実行します。
すべての行の
order_amount
値を両方のテーブルにクエリし、その結果を異なる RESULTSETs (各テーブルに 1 つずつ) に返します。ASYNC キーワードを使用して、クエリが並行子ジョブとして実行されるように指定します。
RESULTSET ごとに AWAIT ステートメントを実行し、プロシージャがクエリの終了を待ってから処理を進めるようにします。AWAIT が RESULTSET に対して実行されるまで、 RESULTSET のクエリ結果にアクセスすることはできません。
カーソルを使用して、各テーブルの
order_amount
行の合計を計算します。テーブルの合計を加算し、値を返します。
CREATE OR REPLACE PROCEDURE test_sp_async_child_jobs_query()
RETURNS INTEGER
LANGUAGE SQL
AS
DECLARE
accumulator1 INTEGER DEFAULT 0;
accumulator2 INTEGER DEFAULT 0;
res1 RESULTSET DEFAULT ASYNC (SELECT order_amount FROM orders_q1_2024);
res2 RESULTSET DEFAULT ASYNC (SELECT order_amount FROM orders_q2_2024);
BEGIN
AWAIT res1;
LET cur1 CURSOR FOR res1;
OPEN cur1;
AWAIT res2;
LET cur2 CURSOR FOR res2;
OPEN cur2;
FOR row_variable IN cur1 DO
accumulator1 := accumulator1 + row_variable.order_amount;
END FOR;
FOR row_variable IN cur2 DO
accumulator2 := accumulator2 + row_variable.order_amount;
END FOR;
RETURN accumulator1 + accumulator2;
END;
注: SnowSQL、 Classic Console、 execute_stream
または execute_string
メソッドを Python Connector コードで使用している場合は、代わりにこの例を使用してください(SnowSQL、 Classic Console、Python ConnectorでSnowflakeスクリプトを使用する を参照)。
CREATE OR REPLACE PROCEDURE test_sp_async_child_jobs_query()
RETURNS INTEGER
LANGUAGE SQL
AS
$$
DECLARE
accumulator1 INTEGER DEFAULT 0;
accumulator2 INTEGER DEFAULT 0;
res1 RESULTSET DEFAULT ASYNC (SELECT order_amount FROM orders_q1_2024);
res2 RESULTSET DEFAULT ASYNC (SELECT order_amount FROM orders_q2_2024);
BEGIN
AWAIT res1;
LET cur1 CURSOR FOR res1;
OPEN cur1;
AWAIT res2;
LET cur2 CURSOR FOR res2;
OPEN cur2;
FOR row_variable IN cur1 DO
accumulator1 := accumulator1 + row_variable.order_amount;
END FOR;
FOR row_variable IN cur2 DO
accumulator2 := accumulator2 + row_variable.order_amount;
END FOR;
RETURN accumulator1 + accumulator2;
END;
$$;
ストアドプロシージャを呼び出します。
CALL test_sp_async_child_jobs_query();
+--------------------------------+
| TEST_SP_ASYNC_CHILD_JOBS_QUERY |
|--------------------------------|
| 4570 |
+--------------------------------+
例:テーブルに行を挿入する子ジョブの同時実行¶
以下のコードは、 ASYNC キーワードを使用して、テーブルに行を挿入する複数の子ジョブを同時に実行する方法を示しています。
以下のストアドプロシージャは、以下のアクションを実行します。
orders_q3_2024
テーブルが存在しない場合は作成します。テーブルへの挿入結果を保持する2つの RESULTSETs、
insert_1
とinsert_2
を作成します。ストアドプロシージャの引数は、テーブルに挿入される値を指定します。ASYNC キーワードを使用して、挿入が同時実行子ジョブとして実行されるように指定します。
RESULTSET ごとに AWAIT ステートメントを実行し、プロシージャが挿入の終了を待ってから処理を進めるようにします。AWAIT が RESULTSET に対して実行されるまで、 RESULTSET の結果にはアクセスできません。
orders_q3_2024
テーブルのクエリ結果を保持する新しい RESULTSETres
を作成します。クエリの結果を返します。
CREATE OR REPLACE PROCEDURE test_sp_async_child_jobs_insert(
arg1 INT,
arg2 NUMBER(12,2),
arg3 INT,
arg4 NUMBER(12,2))
RETURNS TABLE()
LANGUAGE SQL
AS
BEGIN
CREATE TABLE IF NOT EXISTS orders_q3_2024 (
order_id INT,
order_amount NUMBER(12,2));
LET insert_1 RESULTSET := ASYNC (INSERT INTO orders_q3_2024 SELECT :arg1, :arg2);
LET insert_2 RESULTSET := ASYNC (INSERT INTO orders_q3_2024 SELECT :arg3, :arg4);
AWAIT insert_1;
AWAIT insert_2;
LET res RESULTSET := (SELECT * FROM orders_q3_2024 ORDER BY order_id);
RETURN TABLE(res);
END;
注: SnowSQL、 Classic Console、 execute_stream
または execute_string
メソッドを Python Connector コードで使用している場合は、代わりにこの例を使用してください(SnowSQL、 Classic Console、Python ConnectorでSnowflakeスクリプトを使用する を参照)。
CREATE OR REPLACE PROCEDURE test_sp_async_child_jobs_insert(
arg1 INT,
arg2 NUMBER(12,2),
arg3 INT,
arg4 NUMBER(12,2))
RETURNS TABLE()
LANGUAGE SQL
AS
$$
BEGIN
CREATE TABLE IF NOT EXISTS orders_q3_2024 (
order_id INT,
order_amount NUMBER(12,2));
LET insert_1 RESULTSET := ASYNC (INSERT INTO orders_q3_2024 SELECT :arg1, :arg2);
LET insert_2 RESULTSET := ASYNC (INSERT INTO orders_q3_2024 SELECT :arg3, :arg4);
AWAIT insert_1;
AWAIT insert_2;
LET res RESULTSET := (SELECT * FROM orders_q3_2024 ORDER BY order_id);
RETURN TABLE(res);
END;
$$;
ストアドプロシージャを呼び出します。
CALL test_sp_async_child_jobs_insert(1, 325, 2, 241);
+----------+--------------+
| ORDER_ID | ORDER_AMOUNT |
|----------+--------------|
| 1 | 325.00 |
| 2 | 241.00 |
+----------+--------------+
RESULTSET を使用するその他の例¶
以下は RESULTSET を使った例です。
-
この例では、 RESULTSET を反復する FOR ループの使い方を示します。
-
この例では、 RESULTSET のデータテーブルを返すためにカーソルを使用する方法を示します。
-
この例では、ユーザー入力に基づいて変数をバインドしてテーブルのデータを更新する方法を示します。RESULTSET の行を繰り返し処理するために、条件ロジックを含む FOR ループを使用します。
-
この例では、 RESULTSET を使用してデータを収集し、そのデータをテーブルに挿入して過去の傾向を追跡する方法を示します。