类别:

表函数

RESULT_SCAN

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

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

小技巧

您可以使用 管道运算符 (->>) 而不是此函数来处理先前命令的结果。

另请参阅:

DESCRIBE RESULT (账户和会话 DDL)

语法

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

实参

'query_id' query_index LAST_QUERY_ID()

过去 24 小时内您在任意会话中运行的查询的规范,当前会话中查询的整型索引,或 LAST_QUERY_ID 函数,该函数返回当前会话中查询的 ID。

Snowflake 查询 IDs 是类似 01b71944-0001-b181-0000-0129032279f6 的唯一字符串。

查询索引相对于当前会话中的第一个查询(如果为正)或最近的查询(如果为负)。例如,RESULT_SCAN(-1) 相当于 RESULT_SCAN(LAST_QUERY_ID())

此实参是可选的。如果省略,则默认值为 RESULT_SCAN(-1),它返回最近命令的结果集。

使用说明

  • 如果原始查询是手动运行的,则只有运行原始查询的用户才能使用 RESULT_SCAN 函数处理查询的输出。即使具有 RESULT_SCAN 权限的用户也无法通过调用 ACCOUNTADMIN 访问其他用户的查询结果。

  • 如果原始查询是通过 任务 运行的,则负责该任务的角色(而非特定用户)触发并运行查询。如果用户或任务使用相同的角色运行,则它们可以使用 RESULT_SCAN 来访问查询结果。

  • Snowflake 会将所有查询结果存储 24 小时。此函数仅返回在此时间段内运行的查询的结果。

  • 结果集没有任何与其关联的元数据,因此处理大型结果的速度可能要比查询实际表的速度慢。

  • 包含 RESULT_SCAN 的查询可能包括原始查询中没有的子句,例如筛选条件和 ORDER BY 子句。您可以使用这些子句来缩小或修改结果集的范围。

  • RESULT_SCAN 返回行的顺序不保证与原始查询返回行的顺序相同。您可以在 ORDER 查询中包含 BY RESULT_SCAN 子句,以指定特定顺序。

  • 若要检索特定查询的 ID,请使用下列任一方法:

    Snowsight:

    在以下任一位置,单击提供的链接以显示或复制 ID:

    • Worksheets 中的 Projects 下,运行查询后,Query Details 包含 ID 的链接。

    • Query History 中的 Monitoring 下,每个查询都包含链接形式的 ID。

    SQL:

    调用以下任一函数:

  • 如果 RESULT_SCAN 处理包含重复列名的查询输出(例如,联接具有重叠列名的两个表的查询),RESULT_SCAN 会使用修改后的名称(在原始名称后追加 _1_2 等)引用重复的列。有关示例,请参阅下面的 示例 部分。

  • 通过使用矢量化扫描器查询的 Parquet 文件中的时间戳有时会显示不同时区的时间。使用 CONVERT_TIMEZONE 函数将所有时间戳数据转换为标准时区。

排序规则详细信息

RESULT_SCAN 返回上一条语句的结果时,RESULT_SCAN 将保留其返回值的排序规则规范。

示例

以下示例使用 RESULT_SCAN 函数。

简单示例

检索当前会话中最新查询结果内所有大于 1 的值:

SELECT $1 AS value FROM VALUES (1), (2), (3);
Copy
+-------+
| VALUE |
|-------|
|     1 |
|     2 |
|     3 |
+-------+
SELECT * FROM TABLE(RESULT_SCAN(LAST_QUERY_ID())) WHERE value > 1;
Copy
+-------+
| VALUE |
|-------|
|     2 |
|     3 |
+-------+

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

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;
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();
Copy
+--------------------------------------+
| RETURN_JSON                          |
|--------------------------------------|
| {"keyA": "ValueA", "keyB": "ValueB"} |
+--------------------------------------+

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

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

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

将输出从 VARCHAR 值转换为 VARIANT 值:

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

提取与键 keyB 对应的值:

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

以下示例显示了一种更精简的方法,用于提取与上一个示例中相同的数据。此示例的语句较少,但可读性较差:

CALL return_json();
Copy
+--------------------------------------+
| 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()))
      );
Copy
+---------------+
| JSON_COL:KEYB |
|---------------|
| "ValueB"      |
+---------------+

CALL 的输出使用函数名称作为列名称。您可以在查询中使用该列名。以下示例显示了另一个精简版本,其中按名称而非列编号引用列:

CALL return_json();
Copy
+--------------------------------------+
| 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()))
       );
Copy
+---------------+
| JSON_COL:KEYB |
|---------------|
| "ValueB"      |
+---------------+

具有重复列名称的示例

以下示例演示了在原始查询中存在重复列名时,RESULT_SCAN 实际上引用了备用列名的情况:

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

CREATE TABLE employees (id INT);

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

为两个表加载数据:

INSERT INTO employees (id) VALUES (11);

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;
Copy
+----+-----+-------------+
| ID |  ID | EMPLOYEE_ID |
|----+-----+-------------|
| 11 | 101 |          11 |
+----+-----+-------------+

现在调用 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;
Copy
+----+------+-------------+
| ID | ID_1 | EMPLOYEE_ID |
|----+------+-------------|
| 11 |  101 |          11 |
+----+------+-------------+
语言: 中文