问题 为派生表计算SQL Server ROW_NUMBER()OVER()


在其他一些数据库中(例如DB2或Oracle) ROWNUM),我可以省略 ORDER BY 排名函数中的子句 OVER() 条款。例如:

ROW_NUMBER() OVER()

当与有序派生表一起使用时,这尤其有用,例如:

SELECT t.*, ROW_NUMBER() OVER()
FROM (
    SELECT ...
    ORDER BY
) t

如何在SQL Server中进行模拟?我找到了人们使用 这个  ,但这是错误的,因为它对于来自派生表的顺序会表现得非确定:

-- This order here ---------------------vvvvvvvv
SELECT t.*, ROW_NUMBER() OVER(ORDER BY (SELECT 1))
FROM (
    SELECT TOP 100 PERCENT ...
    -- vvvvv ----redefines this order here
    ORDER BY
) t

一个具体的例子(可以看到 SQLFiddle):

SELECT v, ROW_NUMBER() OVER (ORDER BY (SELECT NULL)) RN
FROM (
  SELECT TOP 100 PERCENT 1 UNION ALL
  SELECT TOP 100 PERCENT 2 UNION ALL
  SELECT TOP 100 PERCENT 3 UNION ALL
  SELECT TOP 100 PERCENT 4
  -- This descending order is not maintained in the outer query
  ORDER BY 1 DESC
) t(v)

此外,我不能重用派生表中的任何表达式来重现 ORDER BY 在我的情况下,因为派生表可能不可用,因为它可能由某些外部逻辑提供。

那我该怎么办呢?我可以这样做吗?


3886
2017-09-23 14:24


起源

怎么样 SELECT NULL?它还会给出无效结果吗? - John Woo
@ 491243:是的。好吧,结果显然是“有效的”,但我想知道一个空的行为 OVER() 是非常明确的,或者如果这在DB2上巧合......我将准备一个SQL小提琴来说明这一点 - Lukas Eder
你需要将row_number放在内部查询上,所以如果你不能修改它,我认为你运气不好。 - Laurence
@Laurence:有两个问题。 1)我不一定有权访问内部查询,2)内部查询可能包含 DISTINCT,如果添加 ROW_NUMBER() 会改变内部查询的语义。 - Lukas Eder
嗯,这就是我 认为 同样。但是我想 知道 ;-) - Lukas Eder


答案:


Row_Number() OVER (ORDER BY (SELECT 1)) 技巧应该  被视为避免改变基础数据顺序的一种方法。它只是避免使服务器执行额外和不需要的排序的一种方法(它可能仍然执行排序,但与按列排序相比,它将花费最小的可能性)。

SQL服务器中的所有查询 绝对必须 有一个 ORDER BY 最外层查询中的子句,用于以保证方式可靠地排序结果。

关系数据库中不存在“保留原始顺序”的概念。必须始终将表和查询视为无序,除非是 ORDER BY 子句在最外面的查询中指定。

您可以尝试相同的无序查询100,000次并始终以相同的顺序接收它,因此相信您可以依赖于所述排序。但那将是一个错误,因为有一天,某些事情会发生变化而且不会有你期望的顺序。一个示例是当数据库升级到新版本的SQL Server时 - 这导致许多查询更改其排序。但它不一定是那么大的改变。添加或删除索引的东西可能会导致差异。还有更多:安装Service Pack。分区表。创建包含相关表的索引视图。达到一些临界点,选择扫描而不是搜索。等等。

除非您说“服务器,否则不要依赖于订购结果 ORDER BY”。


10
2017-09-23 17:48



我不完全同意你的评估。以甲骨文为例 ROWNUM, 例如。它确保在每一行产生实际的行号,这是非常神奇的。这使得它成为非关系的伪列,因为它可以在“语义上具有挑战性”的情况下访问,例如,该 WHERE 条款。类似的“怪异”Oracle功能是 FOR UPDATE SKIP LOCKED,它颠倒了SQL子句的语义。但是SQL无论如何都不是100%的关系,所以我认为可能有一种类似的,可靠的方法来访问SQL Server中给定表引用的具体,具体化的元组顺序。 - Lukas Eder
不幸的是,Lukas,我知道无法获得“原始订单”。 Oracle确实提供了一些有用的功能,所以我可能夸大了所有关系数据库的情况。不过,我确实相信我已经适应了SQL Server(当然,如果有必要,可以随时做好更正)。 - ErikE
如果需要原始表顺序,请使用表中指定顺序的列,然后按顺序排序。在SQL Server中,如果按聚簇索引或非聚簇索引进行排序,并且只选择该索引中的列,则它不会执行排序操作。因此,它与具有“自然”顺序(即,没有额外的处理工作)相同。 - siride
Erik,是的,我担心你已经适应了SQL Server :-)我注意到对这些SQL功能强加一个明确的语义是多么严格,这可能是好的,大多数情况下。 @siride:我想你可能没有完全理解我的问题。 - Lukas Eder