- Categories:
RESULT_SCAN¶
Returns the result set of a previous command (within 24 hours of when you executed the query) as if the result was a table. This is particularly useful if you want to process the output from any of the following:
SHOW or DESC[RIBE] command that you executed.
Query you executed on metadata or account usage information, such as Snowflake Information Schema or Account Usage.
The result of a stored procedure that you called.
As an alternative to using RESULT_SCAN, you can call a stored procedure that returns tabular data in the FROM clause of a SELECT statement.
The command/query can be from the current session or any of your other sessions, including past sessions, as long as the 24 hour period has not elapsed. This period is not adjustable. For more details, see Using Persisted Query Results.
- See also:
DESCRIBE RESULT (Account & Session DDL)
Syntax¶
RESULT_SCAN ( { '<query_id>' | <query_index> | LAST_QUERY_ID() } )
Arguments¶
query_id
orquery_index
orLAST_QUERY_ID()
A specification of a query you executed within the last 24 hours in any session, an integer index of a query in the current session, or the LAST_QUERY_ID function, which returns the ID of a query within your current session.
Snowflake query IDs are unique strings that resemble
01b71944-0001-b181-0000-0129032279f6
.Query indexes are relative to the first query in the current session (if positive) or to the most recent query (if negative). For example, RESULT_SCAN(-1) is equivalent to RESULT_SCAN(LAST_QUERY_ID()).
Usage notes¶
If the original query is executed manually, only the user who runs the original query can use the RESULT_SCAN function to process the output of the query. Even a user with the ACCOUNTADMIN privilege cannot access the results of another user’s query by calling RESULT_SCAN.
If the original query is executed via a task, the role that owns the task, instead of a specific user, triggers and runs the query. If a user or a task is operating with the same role, they can use RESULT_SCAN to access the query results.
Snowflake stores all query results for 24 hours. This function only returns results for queries executed within this time period.
Result sets do not have any metadata associated with them, so processing large results might be slower than if you were querying an actual table.
The query containing the RESULT_SCAN can include clauses, such as filters and ORDER BY clauses, that were not in the original query. This allows you to narrow down or modify the result set.
A RESULT_SCAN is not guaranteed to return rows in the same order as the original query returned the rows. You can include an ORDER BY clause with the RESULT_SCAN query to specify a specific order.
To retrieve the ID for a specific query, use any of the following methods:
- Classic Console:
In either of the following locations, click the provided link to display/copy the ID:
- SQL:
Execute either of the following functions:
QUERY_HISTORY , QUERY_HISTORY_BY_* table function.
LAST_QUERY_ID function (if the query was executed in the current session).
For example:
SELECT LAST_QUERY_ID(-2);
Note that this is equivalent to using LAST_QUERY_ID as the input for RESULT_SCAN.
If RESULT_SCAN processes query output that contained duplicate column names (for example, a query that JOINed two tables that have overlapping column names), then RESULT_SCAN references the duplicate columns with modified names, appending “_1”, “_2”, etc. to the original name. For an example, see the Examples section below.
Timestamps in Parquet files queried using the vectorized scanner sometimes display the time in a different time zone. Use the CONVERT_TIMEZONE function to convert to a standard time zone for all timestamp data.
Collation details¶
When RESULT_SCAN
returns the results of the previous statement, RESULT_SCAN
preserves the
collation specification(s) of the values that it returns.
Examples¶
Simple examples¶
Retrieve all values greater than 1
from the result of your most recent query in the current session:
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 | +-------+
Retrieve all values from your second most recent query in the current session:
SELECT * FROM table(RESULT_SCAN(LAST_QUERY_ID(-2)));
Retrieve all values from your first query in the current session:
SELECT * FROM table(RESULT_SCAN(LAST_QUERY_ID(1)));
Retrieve the values from the c2
column in the result of the specified query:
SELECT c2 FROM TABLE(RESULT_SCAN('ce6687a4-331b-4a57-a061-02b2b0f0c17c'));
Examples using DESCRIBE and SHOW commands¶
Process the result of a DESCRIBE USER command to retrieve
particular fields of interest, for example the user’s default role. Note that
because the output column names from the DESC USER
command were generated
in lowercase, the commands use delimited identifier notation (double quotes) around
the column names in the query to ensure that the column names in the query
match the column names in the output that was scanned.
DESC USER jessicajones; SELECT "property", "value" FROM TABLE(RESULT_SCAN(LAST_QUERY_ID())) WHERE "property" = 'DEFAULT_ROLE' ;
Process the result of a SHOW TABLES command to extract empty tables that are older than 21 days. The SHOW command generates lowercase column names, so the command quotes the names and use matching case:
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";
Process the result of a SHOW TABLES command to extract the tables in descending order of size. This example also illustrates using a UDF to show table size in a slightly more human-readable format.
-- 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;
Examples using a stored procedure¶
Stored procedure calls return a value. However, this value cannot be processed directly because you cannot embed a
stored procedure call in another statement. To work around this limitation, you can use RESULT_SCAN
to process the
value returned by a stored procedure. A simplified example is below:
First, create a procedure that returns a “complicated” value (in this case, a string that contains JSON-compatible data) that can be processed after it has been returned from the
CALL
.CREATE OR REPLACE PROCEDURE return_JSON() RETURNS VARCHAR LANGUAGE JavaScript AS $$ return '{"keyA": "ValueA", "keyB": "ValueB"}'; $$ ;Second, call the procedure:
CALL return_JSON(); +--------------------------------------+ | RETURN_JSON | |--------------------------------------| | {"keyA": "ValueA", "keyB": "ValueB"} | +--------------------------------------+The next three steps extract the data from the result set.
Get the first (and only) column:
SELECT $1 AS output_col FROM table(RESULT_SCAN(LAST_QUERY_ID())); +--------------------------------------+ | OUTPUT_COL | |--------------------------------------| | {"keyA": "ValueA", "keyB": "ValueB"} | +--------------------------------------+Convert the output from a VARCHAR to a VARIANT:
SELECT PARSE_JSON(output_col) AS JSON_COL FROM table(RESULT_SCAN(LAST_QUERY_ID())); +---------------------+ | JSON_COL | |---------------------| | { | | "keyA": "ValueA", | | "keyB": "ValueB" | | } | +---------------------+Extract the value that corresponds to the key “keyB”:
SELECT JSON_COL:keyB FROM table(RESULT_SCAN(LAST_QUERY_ID())); +---------------+ | JSON_COL:KEYB | |---------------| | "ValueB" | +---------------+
Here is a more compact way to extract the same data that was extracted in the previous example. This example has fewer statements, but is harder to read:
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" | +---------------+
The output from the CALL uses the function name as the column name. For example:
+--------------------------------------+ | RETURN_JSON | +--------------------------------------+ | {"keyA": "ValueA", "keyB": "ValueB"} | +--------------------------------------+
We can use that column name in the query. Here’s one additional compact version, in which the column is referenceed by name rather than the column number:
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" | +---------------+
Example with duplicate column names¶
The following example shows that RESULT_SCAN effectively references alternate column names when there are duplicate column names in the original query:
Create two tables that have at least one column with the same name:
CREATE TABLE employees (id INT);CREATE TABLE dependents (id INT, employee_id INT);Load data into the two tables:
INSERT INTO employees (id) VALUES (11);INSERT INTO dependents (id, employee_id) VALUES (101, 11);Now execute a query for which the output will contain two columns with the same name:
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 | +----+-----+-------------+Note that the output in Snowsight is different than the output shown above, since Snowsight handles duplicate column names automatically.
Now call RESULT_SCAN to process the results of that query. Note that if different columns that have the same name in the results, RESULT_SCAN uses the original name for the first column and assigns the second column a modified name that is unique. To make the name unique, RESULT_SCAN appends the suffix “_<n>” to the name, where “<n>” is the next number available that produces a name that is different from the names of the previous columns.
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 | +----+------+-------------+