在雪花中使用嵌套窗口函数
Posted
技术标签:
【中文标题】在雪花中使用嵌套窗口函数【英文标题】:Using nested window function in Snowflake 【发布时间】:2021-04-08 21:34:06 【问题描述】:我已经看到很多关于这个一般错误的问题,但我不明白为什么会出现它,可能是因为嵌套的窗口函数......
通过以下查询,我得到了Col_C
、Col_D
、...以及我尝试过的几乎所有内容的错误
SQL 编译错误:[eachColumn] 不是有效的按表达式分组
SELECT
Col_A,
Col_B,
FIRST_VALUE(Col_C) IGNORE NULLS OVER (PARTITION BY Col_A, Col_B
ORDER BY Col_TimeStamp ASC
ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW),
MAX(Col_D) OVER (PARTITION BY Col_A, Col_B
ORDER BY Col_TimeStamp ASC
ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW),
FIRST_VALUE(CASE WHEN Col_T = 'testvalue'
THEN LAST_VALUE(Col_E) IGNORE NULLS OVER (PARTITION BY Col_A, Col_B
ORDER BY Col_TimeStamp DESC
ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW)
ELSE NULL END) IGNORE NULLS
OVER (PARTITION BY Col_A, Col_B
ORDER BY Col_TimeStamp ASC
ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW)
FROM mytable
那么,有没有在 Snowflake 中使用嵌套窗口函数的方法(使用 case when ...
),如果有,我怎么/我做错了什么?
【问题讨论】:
从选择中选择? @astentx :是的,是这个问题吗? 我只见过一个数据库 (Exasol) 可以在同一级别上重用本地计算(我的同事告诉我 Teradata 也可以做到这一点)。在大多数 DBMS 中,这需要使用 CTE(WITH
子句)或FROM
中的子查询逐步重写查询,其中分析函数不使用内部分析函数。因此,在每个新的select
statement 中,您都使用上一级分析函数的结果
您应该提供样本数据、期望的结果以及您想要做什么的清晰解释。嵌套的窗口函数对编译器没有意义。它们对我来说也没有意义。
这对我来说非常有意义(这是我在雪花中写的小版本)如果有一个 GROUP BY,那么上下文将是模糊的,此时第二层窗口函数需要上下文来了解“什么是什么”。但是这里产生了相同数量的行。 AKA 这只是 SF 允许的完美可爱的函数式编程。
【参考方案1】:
所以解构你的逻辑以表明它是导致问题的第二个 FIRST_VALUE
WITH data(Col_A,Col_B,Col_c,col_d, Col_TimeStamp, col_t,col_e) AS (
SELECT * FROM VALUES
(1,1,1,1,1,'testvalue',10),
(1,1,2,3,2,'value',11)
)
SELECT
Col_A,
Col_B,
FIRST_VALUE(Col_C) IGNORE NULLS OVER (PARTITION BY Col_A, Col_B
ORDER BY Col_TimeStamp ASC
ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW) as first_c,
MAX(Col_D) OVER (PARTITION BY Col_A, Col_B
ORDER BY Col_TimeStamp ASC
ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW),
LAST_VALUE(Col_E) IGNORE NULLS OVER (PARTITION BY Col_A, Col_B
ORDER BY Col_TimeStamp DESC
ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW) as last_e,
IFF(Col_T = 'testvalue', last_e, NULL) as if_test_last_e
/*,FIRST_VALUE(if_test_last_e) IGNORE NULLS OVER (PARTITION BY Col_A, Col_B
ORDER BY Col_TimeStamp ASC
ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW) as the_problem*/
FROM data
ORDER BY Col_A,Col_B, col_timestamp
;
如果我们取消注释 the_problem
,我们就有了。与 PostgreSQL(我的背景)相比,重用这么多先前的结果/步骤是一种天赋,所以在这里我只是打破了另一个 SELECT 层。
WITH data(Col_A,Col_B,Col_c,col_d, Col_TimeStamp, col_t,col_e) AS (
SELECT * FROM VALUES
(1,1,1,1,1,'testvalue',10),
(1,1,2,3,2,'value',11)
)
SELECT *,
FIRST_VALUE(if_test_last_e) IGNORE NULLS OVER (PARTITION BY Col_A, Col_B
ORDER BY Col_TimeStamp ASC
ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW) as not_a_problem
FROM (
SELECT
Col_A,
Col_B,
FIRST_VALUE(Col_C) IGNORE NULLS OVER (PARTITION BY Col_A, Col_B
ORDER BY Col_TimeStamp ASC
ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW) as first_c,
MAX(Col_D) OVER (PARTITION BY Col_A, Col_B
ORDER BY Col_TimeStamp ASC
ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW),
LAST_VALUE(Col_E) IGNORE NULLS OVER (PARTITION BY Col_A, Col_B
ORDER BY Col_TimeStamp DESC
ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW) as last_e,
IFF(Col_T = 'testvalue', last_e, NULL) as if_test_last_e
,Col_TimeStamp
FROM data
)
ORDER BY Col_A,Col_B, Col_TimeStamp
然后一切正常。如果您 LAG 然后 IFF/FIRST_VALUE 然后 LAG 第二个结果,也会发生这种情况。
【讨论】:
感谢您的意见,但我选择了横向参考,因为 CTE 看起来有点笨拙且不那么可读。 @R3uK CTE 只是为了提供“测试数据”,它不是实际解决方案的一部分,但我喜欢提供可以复制到 Snowflake 并按原样运行的解决方案,所以你可以和他们一起玩,看看它是如何工作的。 “横向参考”也只是您编码的技术名称,我的代码和 Lukasz 代码都在做巫婆,指的是 SELECT 部分中的输出列,而在同一个 SELECT 部分,这不是有效的 ANSI SQL,但对保持代码清洁非常有帮助。【参考方案2】:“我已经看到很多关于这个一般错误的问题,但我不明白我为什么会有它,可能是因为嵌套的窗口函数......”
Snowflake 支持在同一级别重用表达式(有时称为"lateral column alias reference")
写的很好:
SELECT 1+1 AS col1,
col1 *2 AS col2,
CASE WHEN col1 > col2 THEN 'Y' ELSE 'NO' AS col3
...
在标准 SQL 中,您要么必须使用多级查询 (cte),要么使用 LATERAL JOIN。相关:PostgreSQL: using a calculated column in the same query
不幸的是,相同的语法不适用于分析函数(我现在知道任何支持它的 RDMBS):
SELECT ROW_NUMBER() OVER(PARTITION BY ... ORDER BY ...) AS rn
,MAX(rn) OVER(PARTITION BY <different than prev) AS m
FROM tab;
在 SQL 标准 2016 中有一个可选功能:T619 嵌套窗口函数。
这里有一篇文章如何嵌套分析函数查询:Nested window functions in SQL。
这意味着当前嵌套窗口函数的方法是使用派生表/cte:
WITH cte AS (
SELECT ROW_NUMBER() OVER(PARTITION BY ... ORDER BY ...) AS rn
,*
FROM tab
)
SELECT *, MAX(rn) OVER(PARTITION BY <different than prev) AS m
FROM cte
【讨论】:
而 CTE 只是一个子选择,也就是说,在某些时候,您必须明确说明您使用的数据范围,以帮助编译器了解您的意思。 我就是这样走的,因为 CTE 看起来很笨拙且不那么易读,我通过横向引用设法满足了我的需要,并且仍然非常易读和高效;)以上是关于在雪花中使用嵌套窗口函数的主要内容,如果未能解决你的问题,请参考以下文章