当我在手工构建的数据集上使用FIRST_VALUE时,我得到一个结果,当我在由左连接产生的数据集上使用它时,我得到一个不同的结果 - 即使数据集看起来像我要包含完全相同的数据值。我用下面的简单数据集重现了这个问题。
有人可以告诉我,我是否误解了某些事情?
此SQL产生预期结果,FIRST_VALUE为NULL且LAST_VALUE为30。
SELECT
agroup,
aval,
FIRST_VALUE(aval) OVER (PARTITION BY agroup ORDER BY aval ROWS BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING) fv,
LAST_VALUE(aval) OVER (PARTITION BY agroup ORDER BY aval ROWS BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING) lv
FROM
(
SELECT 1 agroup, 10 aval
UNION ALL SELECT 1, NULL
UNION ALL SELECT 1, 30
) T
此SQL使用LEFT JOIN导致与上面相同的数据集,但FIRST_VALUE似乎忽略NULL。
SELECT
agroup,
aval,
FIRST_VALUE(aval) OVER (PARTITION BY agroup ORDER BY aval ROWS BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING) fv,
LAST_VALUE(aval) OVER (PARTITION BY agroup ORDER BY aval ROWS BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING) lv
FROM
(
SELECT
T1.agroup,
T1.akey,
T2.aval
FROM
(
SELECT 1 agroup, 1 akey
UNION ALL SELECT 1, 2
UNION ALL SELECT 1, 3
) T1
LEFT JOIN
(
SELECT 1 akey, 10 aval
UNION ALL SELECT 3,30
) T2 ON T1.akey = T2.akey
) T
我还可以显示使用表变量与CTE时左连接行为是不同的。使用CTE生成数据时,FIRST_VALUE忽略NULL。使用完全相同的SQL但将结果放在表变量或临时表中会导致将NULL考虑在内。
使用CTE,SQL Server结果在FIRST_VALUE确定中不包含NULL:
WITH T AS
(
SELECT
T1.agroup,
T1.akey,
T2.aval
FROM
(
SELECT 1 agroup, 1 akey
UNION ALL SELECT 1, 2
UNION ALL SELECT 1, 3
) T1
LEFT JOIN
(
SELECT 1 akey, 10 aval
UNION ALL SELECT 3,30
) T2 ON T1.akey = T2.akey
)
SELECT
agroup,
aval,
FIRST_VALUE(aval) OVER (PARTITION BY agroup ORDER BY aval ROWS BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING) fv,
LAST_VALUE(aval) OVER (PARTITION BY agroup ORDER BY aval ROWS BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING) lv
FROM
T
但是使用表变量,它确实:
DECLARE @T TABLE (agroup INT,akey INT,aval INT)
INSERT INTO
@T
SELECT
T1.agroup,
T1.akey,
T2.aval
FROM
(
SELECT 1 agroup, 1 akey
UNION ALL SELECT 1, 2
UNION ALL SELECT 1, 3
) T1
LEFT JOIN
(
SELECT 1 akey, 10 aval
UNION ALL SELECT 3,30
) T2 ON T1.akey = T2.akey
SELECT
agroup,
aval,
FIRST_VALUE(aval) OVER (PARTITION BY agroup ORDER BY aval ROWS BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING) fv,
LAST_VALUE(aval) OVER (PARTITION BY agroup ORDER BY aval ROWS BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING) lv
FROM
@T
答案 0 :(得分:7)
提供的示例非常清楚地表明FIRST_VALUE()
分析函数的实现存在不一致。
取决于FROM
子句中的基础表是一个基表(或临时或表变量,甚至是动态创建的派生表),在一种情况下创建派生表(或cte)通过在第二种情况下在飞行表上创建的两个LEFT JOIN
,结果是不同的。似乎NULL
值在第二种情况下被忽略或被视为高值。
它们不应该不同,因为SQL查询的结果不应该取决于FROM
子句如何获取它为SELECT
子句提供的表的值,还因为文档OVER
条款清楚地说明应如何处理NULL
值:
order_by_expression
指定要排序的列或表达式。 order_by_expression只能引用FROM子句提供的列。不能指定整数来表示列名或别名。
...
ASC | DESC
指定指定列中的值应按升序或降序排序。 ASC是默认排序顺序。 空值被视为可能的最低值。
因此,根据SQL-Server文档,正确的结果是不忽略NULL值的结果。任何其他结果都不应该发生,因为它确实发生了,这是一个错误。
我建议您在最新版本(而不仅仅是在RTM中)进行测试,因为它可能已经在某些服务包或更新中被识别和更正,如果它仍然存在(或者如果您没有新版本)可用)将此提交为Connect站点中的错误。
更新
为了将来参考,该错误由OP提交。链接是:Connect item和(我们的)@Aaron Bertrand在那里评论说它也出现在大多数当前的SQL 2014版本中。
答案 1 :(得分:-1)
对这篇文章有点迟到的回答,不过要分享一下。
您可以按标志使用顺序来降级"降级"空值。
所以在你的情况下......你可以使用
... FIRST_VALUE(aval)OVER(分组由agroup ORDER BY (iif(aval为null,1,0)),aval 在无限制前进和无限制下行之间的行程)fv ...
(注意我使用值1作为空值,因为它应该对字段进行排序,因此非空值将优先)
干杯 - 洛杉矶。