类别:

表函数

RESULT_SCAN

返回前一条命令的结果集(在执行查询的 24 小时内),就好像结果是一个表一样。如果要处理以下任何一项的输出,这会非常有用:

命令/查询可以来自当前会话或任何其他会话,包括过去的会话,只要未超过 24 小时的期限即可。此期限不可调整。有关更多详细信息,请参阅 使用持久化查询结果

另请参阅:

DESCRIBE RESULT (账户和会话 DDL)

语法

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

实参

query_id LAST_QUERY_ID()

(过去 24 小时内在任意会话中)您执行的查询的 ID,或者 LAST_QUERY_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 Worksheet 选项卡 中,执行查询后,结果包含 ID。

    • History History 选项卡 中,每个查询都包含链接形式的 ID。

    SQL:

    执行以下任一函数:

  • 如果 RESULT_SCAN 处理包含重复列名的查询输出(例如,对具有重叠列名的两个表执行 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

从当前会话中第二新的查询检索所有值:

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 来处理存储过程返回的值。下面是一个经过简化的示例:

首先创建一个返回“复杂”值(本例中为包含 JSON 兼容数据的字符串)的过程,该值可在从 CALL 返回后进行处理。

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

接下来的三个步骤从结果集中提取数据。

获取第一列(也是唯一的列):

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 实际上引用了备用列名的情况:

创建两个至少有一个列同名的表:

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

为两个表加载数据:

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

现在执行一个查询,其输出将包含两个同名列:

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 会为第一列使用原始名称,并为第二列分配唯一的经过修改的名称。为保证名称唯一,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
语言: 中文