SQL Server 2012中的LAST_VALUE返回了奇怪的结果

时间:2015-04-30 15:00:27

标签: sql sql-server sql-server-2012

当我尝试从SQL Server 2012中的表中获取LAST_VALUE时,我得到了一个奇怪的结果。

这是我的表

PK | Id1 | Id2
1  | 2   | 5
2  | 2   | 6
3  | 2   | 5
4  | 2   | 6 

这是我的查询

SELECT
    Id1, Id2, LAST_VALUE(PK) OVER (PARTITION BY Id1 ORDER BY Id2) AS LastValue
FROM
    @Data

这是我期待的结果

Id1 | Id2  | LastValue
2   | 5    | 3
2   | 5    | 3
2   | 6    | 4
2   | 6    | 4

这就是我收到的内容

Id1 | Id2  | LastValue
2   | 5    | 3
2   | 5    | 3
2   | 6    | 2
2   | 6    | 2

以下是问题的演示 http://sqlfiddle.com/#!6/5c729/1

我的查询有什么问题吗?

3 个答案:

答案 0 :(得分:3)

SQL Server不知道或不关心将行插入表中的顺序。如果您需要特定订单,请始终使用ORDER BY。在您的示例中,ORDER BY不明确,除非您将PK包含在ORDER BY中。此外,如果你不小心,LAST_VALUE函数可以返回奇数结果 - 见下文。

您可以使用MAXLAST_VALUESQLFiddle)获得预期结果。在这种情况下它们是等价的:

SELECT
    PK, Id1, Id2
    ,MAX(PK) OVER (PARTITION BY Id1, Id2) AS MaxValue
    ,LAST_VALUE(PK) OVER (PARTITION BY Id1, Id2 ORDER BY PK rows between unbounded preceding and unbounded following) AS LastValue
FROM
    Data
ORDER BY id1, id2, PK

无论最初将行插入表中的顺序如何,此查询的结果都是相同的。您可以尝试在小提琴中以不同的顺序放置INSERT个语句。它不会影响结果。

此外,LAST_VALUE的行为并不像您在默认窗口中直观地预期的那样(当ORDER BY子句中只有OVER时)。默认窗口为ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW,而您预计它为ROWS BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING。这是一个good explanation的答案。此SO答案的链接位于LAST_VALUE的MSDN页面上。因此,一旦在查询中明确指定了行窗口,它就会返回所需的内容。

如果您想知道在表中插入行的顺序,我认为,最简单的方法是使用IDENTITY。因此,您的表的定义将更改为:

CREATE TABLE Data 
(PK INT IDENTITY(1,1) PRIMARY KEY,
Id1 INT,
Id2 INT)

当您INSERT进入此表时,您不需要指定PK的值,服务器会自动生成它。它保证生成的值是唯一的并且正在增长(使用正增量参数),即使您有许多客户端同时同时插入表中。生成的值之间可能存在间隙,但生成的值的相对顺序将告诉您在哪行之后插入了哪一行。

答案 1 :(得分:1)

  

依赖由底层数据库引擎的特定实现引起的隐式顺序绝不是一个好主意。

我不知道为什么,运行查询

SELECT * FROM @Data ORDER BY Id2

结果将是

+----+-----+-----+
| PK | id1 | id2 |
+----+-----+-----+
|  1 |   2 |   5 |
|  3 |   2 |   5 |
|  4 |   2 |   6 |
|  2 |   2 |   6 |
+----+-----+-----+

这意味着SQL Server以与插入顺序不同的方式决定了行的顺序。

这就是LAST_VALUE行为与预期不同的原因,但与SQL Server排序方法一致。

但是SQL Server如何对数据进行排序?

我们得到的最佳答案是question的接受答案(从我在答案开始时我从哪里开始)。

答案 2 :(得分:0)

SELECT  Id1
        , Id2
        , LAST_VALUE(PK) 
            OVER (PARTITION BY Id1 
            ORDER BY Id2 DESC) AS LastValue
FROM    Data
ORDER BY Id2 ASC

结果

Id1 Id2 LastValue
2   5   3
2   5   3
2   6   4
2   6   4