LAST_VALUE,CURRENT ROW和NULL的意外结果

时间:2014-10-06 12:51:17

标签: sql-server sql-server-2012 window-functions sql-server-2014

我有以下数据:

CREATE TABLE #Transfers (
    AddedOn DATETIME2 NOT NULL,
    EmpID INT NOT NULL,
    NewDeptID INT NULL
)

INSERT INTO #Transfers
VALUES 
    ('2013-12-17 17:18:54.3499987', 19, 36),
    ('2013-12-18 13:02:34.1168087', 19, NULL),
    ('2014-01-28 11:41:55.8755928', 22, 100),
    ('2014-02-05 10:36:36.3645703', 22, NULL),
    ('2014-02-16 00:00:00.0000000', 22, 37),
    ('2014-02-17 00:00:00.0000000', 22, NULL)

对于每一行,我试图获取最新的非空NewDeptID(直到该行):

SELECT *,
    LAST_VALUE(NewDeptID) OVER (
        PARTITION BY EmpID
        ORDER BY IIF(NewDeptID IS NULL,0,1), AddedOn
        ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW
    ) AS CurrentDeptID
FROM #Transfers
ORDER BY EmpID, AddedOn

如果我理解正确,应排除空值,因为它们是窗口中的第一个值 - IIF(NewDeptID IS NULL,0,1)

我希望如下:

AddedOn                        EmpID  NewDeptID  CurrentDeptID
2013-12-17 17:18:54.3499987    19     36         36
2013-12-18 13:02:34.1168087    19     NULL       36
2014-01-28 11:41:55.8755928    22     100        100
2014-02-05 10:36:36.3645703    22     NULL       100
2014-02-16 00:00:00.0000000    22     37         37
2014-02-17 00:00:00.0000000    22     NULL       37

而是忽略LAST_VALUE中的ORDER BY子句,当前行包含NULL时返回NULL:

AddedOn                        EmpID  NewDeptID  CurrentDeptID
2013-12-17 17:18:54.3499987    19     36         36
2013-12-18 13:02:34.1168087    19     NULL       NULL --
2014-01-28 11:41:55.8755928    22     100        100
2014-02-05 10:36:36.3645703    22     NULL       NULL --
2014-02-16 00:00:00.0000000    22     37         37
2014-02-17 00:00:00.0000000    22     NULL       NULL --

我在SQL Server 2012和2014中获得了相同的结果。

这是SQL Server中的错误,还是我在窗口函数语法中遗漏了什么?


注意:如果我展开窗口以包含整个分区,则忽略NULL:

SELECT *,
    LAST_VALUE(NewDeptID) OVER (
        PARTITION BY EmpID
        ORDER BY IIF(NewDeptID IS NULL,0,1), AddedOn
        ROWS BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING
    ) AS CurrentDeptID
FROM #Transfers
ORDER BY EmpID, AddedOn

结果:

AddedOn                        EmpID  NewDeptID  CurrentDeptID
2013-12-17 17:18:54.3499987    19     36         36
2013-12-18 13:02:34.1168087    19     NULL       36
2014-01-28 11:41:55.8755928    22     100        37
2014-02-05 10:36:36.3645703    22     NULL       37
2014-02-16 00:00:00.0000000    22     37         37
2014-02-17 00:00:00.0000000    22     NULL       37

1 个答案:

答案 0 :(得分:3)

不,你还不太清楚,窗口功能是如何工作的。 ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROWORDER BY IIF... 后应用,因此首先排序行(首先是NULL行,然后是所有其余行),然后是{{ 1}}限制被应用。所以,这对你的问题永远不会有用。

详细信息,ROWS ...子句会根据OVERPARTITION创建这些" windows"。
因此,例如对于ORDER BY行,AddedOn = '2014-02-17 00:00:00.0000000'是上面的行和它自己(标有ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW的两行):

<------

因此,AddedOn EmpID IIF() NewDeptID -- partition EmpID = 19 2013-12-18 13:02:34.1168087 19 0 NULL 2013-12-17 17:18:54.3499987 19 1 36 -- partition EmpID = 22 2014-02-05 10:36:36.3645703 22 0 NULL <--- 2014-02-17 00:00:00.0000000 22 0 NULL <--- this is the LAST_VALUE 2014-01-28 11:41:55.8755928 22 1 100 2014-02-16 00:00:00.0000000 22 1 37 列会获得以下值:

CurrentDeptID

然后根据外部AddedOn EmpID IIF() NewDeptID CurrentDeptID -- partition EmpID = 19 2013-12-18 13:02:34.1168087 19 0 NULL NULL 2013-12-17 17:18:54.3499987 19 1 36 36 -- partition EmpID = 22 2014-02-05 10:36:36.3645703 22 0 NULL NULL 2014-02-17 00:00:00.0000000 22 0 NULL NULL 2014-01-28 11:41:55.8755928 22 1 100 100 2014-02-16 00:00:00.0000000 22 1 37 37

重新排序以获得最终结果

要解决此问题,可以使用相关子查询:

ORDER BY

SQL-Fiddle

进行测试

如果SELECT *, ( SELECT TOP (1) NewDeptID FROM #Transfers AS ti WHERE ti.EmpID = t.EmpID AND ti.NewDeptID IS NOT NULL AND ti.AddedOn <= t.AddedOn ORDER BY AddedOn DESC ) AS CurrentDeptID FROM #Transfers AS t ORDER BY EmpID, AddedOn ; 函数可以忽略空值,那么您尝试做的事实际上是有意义的。这可以通过 LAST_VALUE() 来完成,但遗憾的是,此功能尚未在SQL-Server中实现。您可以在 Oracle (fiddle-2)

中查看它的工作原理
IGNORE NULLS

使用在SQL-Server中工作的窗口函数的另一种方法是首先计算当前行之前的非空SELECT AddedOn, EmpID, NewDeptID, LAST_VALUE(NewDeptID) IGNORE NULLS -- check this OVER ( PARTITION BY EmpID ORDER BY AddedOn ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW ) AS CurrentDeptID FROM Transfers ORDER BY EmpID, AddedOn ; 。您可以在 fiddle-3

进行测试
NewDeptID