如何使用 Snowflake Javascript 存储过程或函数遍历表中的所有列?

Posted

技术标签:

【中文标题】如何使用 Snowflake Javascript 存储过程或函数遍历表中的所有列?【英文标题】:How to iterate over all columns in a table using Snowflake Javascript Stored Procedure or Function? 【发布时间】:2020-07-29 17:26:54 【问题描述】:

我在 Snowflake 中有一个包含 100 多列的表,我试图获取每列中所有不同值的计数,并最终将每列的所有计数连接到一个表中。如果我只在一个专栏上做它会是这样的:

SELECT DISTINCT "AGE", count(*) AS "Frequency"
FROM 
    db.schema.tablename
WHERE 
    "SURVEYDATE" < "2019-07-29"
GROUP BY
    AGE;

我知道这在 Python 中做起来有些微不足道(也许我应该只在 PySpark 中做,我愿意接受建议),但我认为这对我的团队来说既易于使用又可以更快地完成在 3 亿行中,我想使用 Snowflake javascript 程序语言来执行以下操作:

create or replace procedure column_counts(table)
returns array
language javascript
as
$$
var num_columns = //get number of columns
var columns = [list of columns]
var results_array = [];

for (i = 0; i < num_columns; i++) 
    var col_count = snowflake.createStatement( sqlText: 'SELECT DISTINCT columns[i], count(*) AS "Frequency" FROM 
    db.schema.tablename WHERE "SURVEYDATE" < "2019-07-29" GROUP BY columns[i]' ).execute(); //This returns a table of all distinct values in that column and their counts
    results_array.push([columns[i], col_count]) //I then want an array like [column_name[0...i], distinct_value[0....n], frequency]
    return results_array;
$$
;
CALL column_counts();

我对在 Snowflake 和整个 Snowflake 中使用这种程序语言还是很陌生,因此绝对愿意接受有关如何最好地做到这一点的建议,并以可重复的方式为每个月出现的新表提供建议。

【问题讨论】:

【参考方案1】:

没有任何程序代码也是可能的。例如使用 JSON:

WITH cte AS ( -- here goes the table/query/view
  SELECT TOP 100 OBJECT_CONSTRUCT(*) AS json_payload
  FROM SNOWFLAKE_SAMPLE_DATA.TPCH_SF1.ORDERS
)
SELECT f.KEY, 
      COUNT(DISTINCT f."VALUE") AS frequency, 
      LISTAGG(DISTINCT  f."VALUE" ,',') AS distinct_values  -- debug
FROM cte
, LATERAL FLATTEN (input => json_payload) f
-- WHERE f.KEY IN ('column_name1', 'column_name2', ...) -- only specific columns
GROUP BY f.KEY;

输出:

+-----------------+-----------+------------------------------------------------+
|       KEY       | FREQUENCY |                DISTINCT_VALUES                 |
+-----------------+-----------+------------------------------------------------+
| O_ORDERPRIORITY |         5 | 2-HIGH,1-URGENT,5-LOW,4-NOT SPECIFIED,3-MEDIUM |
| O_ORDERSTATUS   |         3 | P,O,F                                          |
| O_SHIPPRIORITY  |         1 | 0                                              |
| ...             |       ... | ....                                           |
+-----------------+-----------+------------------------------------------------+

它是如何工作的:

    使用OBJECT_CONSTRUCT(*)为每一行生成 JSON

    将 JSON 扁平化为键/值

    按键分组并应用特定的聚合函数COUNT/COUNT(DISTINCT )/LISTAGG/MIN/MAX/...


提供每个列/值分布的版本:

WITH cte AS (
  SELECT TOP 100 OBJECT_CONSTRUCT(*) AS json_payload
  FROM SNOWFLAKE_SAMPLE_DATA.TPCH_SF1.ORDERS
)
SELECT f.KEY, f."VALUE", COUNT(*) AS frequency
FROM cte
, LATERAL FLATTEN (input => json_payload) f
-- WHERE f.KEY IN ('column_name1', 'column_name2', ...) -- only specific columns
GROUP BY f.KEY, f."VALUE"
ORDER BY f.KEY, f."VALUE";

【讨论】:

太棒了,谢谢!我不知道如何使用 cte,我觉得我的眼睛已经打开了。我猜我可以使查询变得尽可能复杂,并且可以使用 IFF 语句等特定的特定列集,我不会有任何问题? 如果我想做 100 列中的 70 列,我该怎么做? @kwarner 您可以将 CTE 视为使用子查询的更好方式(它们也具有其他特性)。因此,您可以在 cte 中执行您喜欢的任何类型的转换,然后将其具体化为 JSON。至于限制列,您可以使用WHERE f.KEY IN ('col1', 'col2', ...)WHERE f.KEY LIKE 'colprefix%'(如果它们有共同模式) 感谢您提供的所有有用信息。最后一个问题,如果我想采用 COUNT(*)/COUNT(DISTINCT(ID)) 这样的百分比,我将如何防止 ID 在这种情况下成为无效标识符? @kwarner 这应该是一个新问题,包含样本数据和所需的结果集。无论如何,也许像: COUNT(*)/COUNT(DISTINCT CASE WHEN f.KEY = 'ID' THEN f."VALUE" END) 会做

以上是关于如何使用 Snowflake Javascript 存储过程或函数遍历表中的所有列?的主要内容,如果未能解决你的问题,请参考以下文章

JavaScript 内存不足错误:超出 UDF 线程内存限制-Snowflake

Snowflake 存储过程中的最大 JavaScript 字符串大小

在没有 JavaScript 的情况下将 Oracle PL/SQL 移植到 Snowflake

在 Snowflake 中处理多个 SQL 语句的存储过程

在我们将其解析为 JSON 之前,Snowflake 如何转义对象数组字符串中的所有特殊字符?

Snowflake Javascript UDTF 中的动态 where 子句