- 类别:
RESULT_SCAN¶
返回前一条命令的结果集(在运行查询的 24 小时内),就好像结果是一个表一样。如果要处理以下任何一项操作的输出,此函数会非常有用:
您运行的 SHOW 或 DESC[RIBE] 命令。
您对元数据或账户使用情况信息运行的查询,例如 Snowflake Information Schema 或 Account Usage。
您 调用 的存储过程的结果。
作为使用 RESULT_SCAN 的替代方案,您可以调用存储过程,该过程可在 SELECT 语句的 FROM 子句 中返回表格数据。
命令或查询可以来自当前会话或任何其他会话,包括过去的会话,只要未超过 24 小时的期限即可。此期限不可调整。有关更多信息,请参阅 使用持久化查询结果。
小技巧
您可以使用 管道运算符 (->>
) 而不是此函数来处理先前命令的结果。
- 另请参阅:
DESCRIBE RESULT (账户和会话 DDL)
语法¶
RESULT_SCAN ( [ { '<query_id>' | <query_index> | LAST_QUERY_ID() } ] )
实参¶
'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:
调用以下任一函数:
LAST_QUERY_ID 函数(如果查询在当前会话中运行)。
例如:
SELECT LAST_QUERY_ID(-2);
这等同于将 LAST_QUERY_ID 用作 RESULT_SCAN 的输入。
如果 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);
+-------+
| VALUE |
|-------|
| 1 |
| 2 |
| 3 |
+-------+
SELECT * FROM TABLE(RESULT_SCAN(LAST_QUERY_ID())) WHERE value > 1;
+-------+
| VALUE |
|-------|
| 2 |
| 3 |
+-------+
从当前会话中第二新的查询检索所有值:
SELECT * FROM TABLE(RESULT_SCAN(LAST_QUERY_ID(-2)));
从当前会话中第一个查询检索所有值:
SELECT * FROM TABLE(RESULT_SCAN(LAST_QUERY_ID(1)));
检索指定查询结果的 c2
列中的所有值:
SELECT c2 FROM TABLE(RESULT_SCAN('ce6687a4-331b-4a57-a061-02b2b0f0c17c'));
使用 DESCRIBE 和 SHOW 命令的示例¶
处理 DESCRIBE USER 命令的结果以检索感兴趣的特定字段,例如用户的默认角色。由于 DESC USER 命令的输出列名以小写形式生成,因此这些命令在查询中的列名周围使用分隔标识符表示法(双引号),以确保查询中的列名与扫描的输出中的列名相匹配。
DESC USER jessicajones;
SELECT "property", "value" FROM TABLE(RESULT_SCAN(LAST_QUERY_ID()))
WHERE "property" = 'DEFAULT_ROLE';
处理 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";
处理 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;
使用存储过程的示例¶
存储过程调用会返回一个值。但此值不能直接处理,因为您不能在另一个语句中嵌入存储过程调用。若要解决此限制,可使用 RESULT_SCAN 来处理存储过程返回的值。下面是一个经过简化的示例:
首先创建一个返回“复杂”值(本例中为包含 JSON 兼容数据的字符串)的过程,该值可在从 CALL 返回后进行处理。
CREATE OR REPLACE PROCEDURE return_json()
RETURNS VARCHAR
LANGUAGE JavaScript
AS
$$
return '{"keyA": "ValueA", "keyB": "ValueB"}';
$$
;
调用过程:
CALL return_json();
+--------------------------------------+
| RETURN_JSON |
|--------------------------------------|
| {"keyA": "ValueA", "keyB": "ValueB"} |
+--------------------------------------+
接下来的三个步骤从结果集中提取数据。
获取第一列(也是唯一的列):
SELECT $1 AS output_col FROM table(RESULT_SCAN(LAST_QUERY_ID()));
+--------------------------------------+
| OUTPUT_COL |
|--------------------------------------|
| {"keyA": "ValueA", "keyB": "ValueB"} |
+--------------------------------------+
将输出从 VARCHAR 值转换为 VARIANT 值:
SELECT PARSE_JSON(output_col) AS json_col FROM TABLE(RESULT_SCAN(LAST_QUERY_ID()));
+---------------------+
| JSON_COL |
|---------------------|
| { |
| "keyA": "ValueA", |
| "keyB": "ValueB" |
| } |
+---------------------+
提取与键 keyB
对应的值:
SELECT json_col:keyB FROM TABLE(RESULT_SCAN(LAST_QUERY_ID()));
+---------------+
| JSON_COL:KEYB |
|---------------|
| "ValueB" |
+---------------+
以下示例显示了一种更精简的方法,用于提取与上一个示例中相同的数据。此示例的语句较少,但可读性较差:
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" |
+---------------+
CALL 的输出使用函数名称作为列名称。您可以在查询中使用该列名。以下示例显示了另一个精简版本,其中按名称而非列编号引用列:
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" |
+---------------+
具有重复列名称的示例¶
以下示例演示了在原始查询中存在重复列名时,RESULT_SCAN 实际上引用了备用列名的情况:
创建两个至少有一个列同名的表:
CREATE TABLE employees (id INT);
CREATE TABLE dependents (id INT, employee_id INT);
为两个表加载数据:
INSERT INTO employees (id) VALUES (11);
INSERT INTO dependents (id, employee_id) VALUES (101, 11);
现在运行一个查询,其输出将包含两个同名列:
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 |
+----+-----+-------------+
现在调用 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 |
+----+------+-------------+