カテゴリ:

テーブル関数

RESULT_SCAN

結果がテーブルであるかのように、以前のコマンドの結果セット(クエリを実行した24時間以内)を返します。これは、次のいずれかからの出力を処理する場合に特に便利です。

24時間が経過していない限り、コマンド/クエリは現在のセッションまたは過去のセッションを含む他のセッションから取得できます。この時間は調整できません。詳細については、 保存済みのクエリ結果の使用 をご参照ください。

こちらもご参照ください:

DESCRIBE RESULT (アカウントとセッション DDL)

構文

RESULT_SCAN ( { '<query_id>'  | LAST_QUERY_ID() } )
Copy

引数

query_id または LAST_QUERY_ID()

実行したクエリのID(任意のセッションの過去24時間以内)または LAST_QUERY_ID 過去のクエリID 関数です。現在のセッション内のクエリの ID を返します。

使用上の注意

  • 元のクエリを手動で実行した場合、元のクエリを実行したユーザーのみが RESULT_SCAN 関数を使用してクエリの出力を処理できます。ACCOUNTADMIN 権限を持つユーザであっても、 RESULT_SCAN を呼び出して他のユーザのクエリの結果にアクセスすることはできません。

  • 元のクエリが タスク を介して実行される場合、特定のユーザーではなく、タスクを所有するロールがクエリをトリガーして実行します。ユーザまたはタスクが同じロールで操作されている場合、 RESULT_SCAN を使用してクエリ結果にアクセスできます。

  • Snowflakeは、すべてのクエリ結果を24時間保存します。この関数は、この期間内に実行されたクエリの結果のみを返します。

  • 結果セットにはメタデータが関連付けられていないため、大きな結果の処理は、実際のテーブルをクエリする場合よりも遅くなる可能性があります。

  • RESULT_SCAN を含むクエリには、元のクエリにはなかったフィルターや ORDER BY句などの句を含めることができます。これにより、結果セットの絞り込みや変更ができます。

  • RESULT_SCAN では、元のクエリが行を返したのと同じ順序で行を返すことが保証されていません。特定の順序を指定するために、 RESULT_SCAN クエリに ORDER BY 句を含めることができます。

  • 特定のクエリの ID を取得するには、次のいずれかの方法を使用します。

    Classic Console:

    次のいずれかの場所で、提供されたリンクをクリックして IDを表示/コピーします。

    • Worksheets ワークシートタブ では、クエリの実行後、結果に IDのリンクが含まれます。

    • History 履歴タブ では、各クエリに ID がリンクとして含まれています。

    SQL:

    次の関数のいずれかを実行します。

  • RESULT_SCAN が重複する列名を含むクエリ出力(たとえば、重複する列名を持つ2つのテーブルを JOINed するクエリ)を処理する場合、 RESULT_SCAN は変更された名前を持つ重複した列を参照し、元の名前に「_1」、「_2」などを追加します。例については、以下の セクションをご参照ください。

  • ベクトル化スキャナーを使用してクエリされたParquetファイルのタイムスタンプが、異なるタイムゾーンの時刻を表示することがありました。すべてのタイムスタンプデータを標準タイムゾーンに変換するには、 CONVERT_TIMEZONE 関数を使用します。

照合の詳細

RESULT_SCAN が前のステートメントの結果を返す場合、 RESULT_SCAN は返される値の照合順序仕様を保持します。

簡単な例

現在のセッションの最新のクエリの結果から 1 より大きいすべての値を取得します。

SELECT $1 AS value FROM VALUES (1), (2), (3);

+-------+
| VALUE |
|-------|
|     1 |
|     2 |
|     3 |
+-------+

SELECT * FROM TABLE(RESULT_SCAN(LAST_QUERY_ID())) WHERE value > 1;

+-------+
| VALUE |
|-------|
|     2 |
|     3 |
+-------+
Copy

現在のセッションで2番目に新しいクエリからすべての値を取得します。

SELECT * FROM table(RESULT_SCAN(LAST_QUERY_ID(-2)));
Copy

現在のセッションの最初のクエリからすべての値を取得します。

SELECT * FROM table(RESULT_SCAN(LAST_QUERY_ID(1)));
Copy

指定されたクエリの結果の c2 列から値を取得します。

SELECT c2 FROM TABLE(RESULT_SCAN('ce6687a4-331b-4a57-a061-02b2b0f0c17c'));
Copy

DESCRIBE および SHOW コマンドを使用した例

DESCRIBE USER コマンドの結果を処理して、関心のある特定のフィールド、例えばユーザーの既定のロールを取得します。 DESC USER コマンドからの出力列名は小文字で生成されたため、コマンドはクエリ内の列名を区切り識別子表記(二重引用符)で囲み、クエリの列名がスキャンしている出力の列名と一致することを確認します。

DESC USER jessicajones;
SELECT "property", "value" FROM TABLE(RESULT_SCAN(LAST_QUERY_ID()))
  WHERE "property" = 'DEFAULT_ROLE'
  ;
Copy

SHOW TABLES コマンドの結果を処理して、21日より古い空のテーブルを抽出します。SHOW コマンドは小文字の列名を生成するため、コマンドは名前を引用符で囲み、大文字と小文字を区別して使用します。

SHOW TABLES;
-- Show the tables that are more than 21 days old and that are empty
-- (i.e. tables that I might have forgotten about).
SELECT "database_name", "schema_name", "name" as "table_name", "rows", "created_on"
    FROM table(RESULT_SCAN(LAST_QUERY_ID()))
    WHERE "rows" = 0 AND "created_on" < DATEADD(day, -21, CURRENT_TIMESTAMP())
    ORDER BY "created_on";
Copy

SHOW TABLES コマンドの結果を処理して、テーブルをサイズの降順に抽出します。また、この例では、 UDF を使用して、テーブルサイズを少し読みやすい形式で表示しています。

-- Show byte counts with suffixes such as "KB", "MB", and "GB".
CREATE OR REPLACE FUNCTION NiceBytes(NUMBER_OF_BYTES INTEGER)
RETURNS VARCHAR
AS
$$
CASE
    WHEN NUMBER_OF_BYTES < 1024
        THEN NUMBER_OF_BYTES::VARCHAR
    WHEN NUMBER_OF_BYTES >= 1024 AND NUMBER_OF_BYTES < 1048576
        THEN (NUMBER_OF_BYTES / 1024)::VARCHAR || 'KB'
   WHEN NUMBER_OF_BYTES >= 1048576 AND NUMBER_OF_BYTES < (POW(2, 30))
       THEN (NUMBER_OF_BYTES / 1048576)::VARCHAR || 'MB'
    ELSE
        (NUMBER_OF_BYTES / POW(2, 30))::VARCHAR || 'GB'
END
$$
;
SHOW TABLES;
-- Show all of my tables in descending order of size.
SELECT "database_name", "schema_name", "name" as "table_name", NiceBytes("bytes") AS "size"
    FROM table(RESULT_SCAN(LAST_QUERY_ID()))
    ORDER BY "bytes" DESC;
Copy

ストアドプロシージャの使用例

ストアドプロシージャコールは値を返します。ただし、ストアドプロシージャコールを別のステートメントに埋め込むことはできないため、この値を直接処理することはできません。この制限を回避するには、 RESULT_SCAN を使用して、ストアドプロシージャによって返される値を処理します。簡単な例を以下に示します。

最初に、 CALL から返された後に処理できる「複雑な」値(この場合は JSON互換データを含む文字列)を返すプロシージャを作成します。

CREATE OR REPLACE PROCEDURE return_JSON()
    RETURNS VARCHAR
    LANGUAGE JavaScript
    AS
    $$
        return '{"keyA": "ValueA", "keyB": "ValueB"}';
    $$
    ;
Copy

次に、プロシージャを呼び出します。

CALL return_JSON();
+--------------------------------------+
| RETURN_JSON                          |
|--------------------------------------|
| {"keyA": "ValueA", "keyB": "ValueB"} |
+--------------------------------------+
Copy

次の3つの手順では、結果セットからデータを抽出します。

最初の(そして唯一の)列を取得します。

SELECT $1 AS output_col FROM table(RESULT_SCAN(LAST_QUERY_ID()));
+--------------------------------------+
| OUTPUT_COL                           |
|--------------------------------------|
| {"keyA": "ValueA", "keyB": "ValueB"} |
+--------------------------------------+
Copy

出力を VARCHAR から VARIANTに変換します。

SELECT PARSE_JSON(output_col) AS JSON_COL FROM table(RESULT_SCAN(LAST_QUERY_ID()));
+---------------------+
| JSON_COL            |
|---------------------|
| {                   |
|   "keyA": "ValueA", |
|   "keyB": "ValueB"  |
| }                   |
+---------------------+
Copy

キー「keyB」に対応する値を抽出します。

SELECT JSON_COL:keyB FROM table(RESULT_SCAN(LAST_QUERY_ID()));
+---------------+
| JSON_COL:KEYB |
|---------------|
| "ValueB"      |
+---------------+
Copy

前の例で抽出されたのと同じデータを抽出する、よりコンパクトな方法を次に示します。この例では、ステートメントは少なくなっていますが、読みづらくなっています。

CALL return_JSON();
+--------------------------------------+
| RETURN_JSON                          |
|--------------------------------------|
| {"keyA": "ValueA", "keyB": "ValueB"} |
+--------------------------------------+
SELECT JSON_COL:keyB 
   FROM (
        SELECT PARSE_JSON($1::VARIANT) AS JSON_COL 
            FROM table(RESULT_SCAN(LAST_QUERY_ID()))
        );
+---------------+
| JSON_COL:KEYB |
|---------------|
| "ValueB"      |
+---------------+
Copy

CALL からの出力は、関数名を列名として使用します。例:

+--------------------------------------+
|              RETURN_JSON             |
+--------------------------------------+
| {"keyA": "ValueA", "keyB": "ValueB"} |
+--------------------------------------+
Copy

クエリでその列名を使用できます。次に、追加のコンパクトバージョンを示します。このバージョンでは、列は列番号ではなく名前で参照されます。

CALL return_JSON();
+--------------------------------------+
| RETURN_JSON                          |
|--------------------------------------|
| {"keyA": "ValueA", "keyB": "ValueB"} |
+--------------------------------------+
SELECT JSON_COL:keyB
        FROM (
             SELECT PARSE_JSON(RETURN_JSON::VARIANT) AS JSON_COL 
                 FROM table(RESULT_SCAN(LAST_QUERY_ID()))
             );
+---------------+
| JSON_COL:KEYB |
|---------------|
| "ValueB"      |
+---------------+
Copy

列名が重複している例

次の例は、元のクエリに重複する列名がある場合、 RESULT_SCAN が代替列名を効果的に参照することを示しています。

同じ名前の列が少なくとも1つある2つのテーブルを作成します。

CREATE TABLE employees (id INT);
Copy
CREATE TABLE dependents (id INT, employee_id INT);
Copy

2つのテーブルにデータをロードします。

INSERT INTO employees (id) VALUES (11);
Copy
INSERT INTO dependents (id, employee_id) VALUES (101, 11);
Copy

次に、出力に同じ名前の2つの列が含まれるクエリを実行します。

SELECT * 
    FROM employees INNER JOIN dependents
        ON dependents.employee_ID = employees.id
    ORDER BY employees.id, dependents.id
    ;
+----+-----+-------------+
| ID |  ID | EMPLOYEE_ID |
|----+-----+-------------|
| 11 | 101 |          11 |
+----+-----+-------------+
Copy

Snowsight での出力は、 Snowsight が重複する列名を自動的に処理するため、上に示した出力とは異なることに注意してください。

次に、 RESULT_SCAN を呼び出して、そのクエリの結果を処理します。結果に同じ名前を持つ異なる列がある場合、 RESULT_SCAN は、最初の列には元の名前を使用し、2番目の列には変更された一意の名前を割り当てます。名前を一意にするために、 RESULT_SCAN は、「_<n>」というサフィックスを名前に付加します。「<n>」は、前の列の名前とは異なる名前を生成する、次に使用可能な数字です。

SELECT id, id_1, employee_id
    FROM TABLE(RESULT_SCAN(LAST_QUERY_ID()))
    WHERE id_1 = 101;
+----+------+-------------+
| ID | ID_1 | EMPLOYEE_ID |
|----+------+-------------|
| 11 |  101 |          11 |
+----+------+-------------+
Copy