【问题标题】:Table-Valued function - Order by is ignored in output表值函数 - 输出中忽略排序依据
【发布时间】:2023-03-11 22:40:01
【问题描述】:

我们正在从 SQL Server 2008 迁移到 SQL Server 2012,并立即注意到我们所有的表值函数不再以正确排序的顺序传递它们的临时表内容。

代码:

INSERT INTO @Customer
        SELECT Customer_ID, Name,
        CASE 
            WHEN Expiry_Date < GETDATE() then 1 
            WHEN Expired = 1 then 1 
            ELSE 0
            END
        from Customer **order by Name**

在 SQL Server 2008 中,此函数返回按名称排序的客户。在 SQL Server 2012 中,它返回未排序的表。 “order by” 在 SQL 2012 中被忽略。

我们是否必须重新编写所有函数以包含sort_id,然后在主应用程序中调用它们时对其进行排序,还是有一个简单的解决方法??

【问题讨论】:

  • 是的,插入仍然出现在计划中,但未反映在结果中。

标签: sql sql-server sql-server-2012 sql-order-by


【解决方案1】:

你原来的方法有两个问题。

  1. 在插入表时,永远无法保证INSERT ... SELECT ... ORDER BY 上的ORDER BY 将是实际插入行的顺序。
  2. 在从中选择时,SQL Server 不保证没有 ORDER BYSELECT 会以任何特定顺序(例如插入顺序)返回行。

在 2012 年,项目 1 的行为似乎发生了变化。现在它通常会忽略 SELECT 语句中的 ORDER BY,它是 INSERT 的来源

DECLARE @T TABLE(number int)

INSERT INTO @T 
SELECT number
FROM master..spt_values
ORDER BY name

2008年计划

2012年计划

行为改变的原因是在以前的版本中,SQL Server 生成了一个计划,该计划在SET ROWCOUNT 0(关闭)和SET ROWCOUNT N 的执行之间共享。排序运算符只是为了确保正确的语义,以防计划由具有非零 ROWCOUNT 集的会话运行。左侧的TOP 运算符是ROWCOUNT TOP

SQL Server 2012 现在为这两种情况生成单独的计划,因此无需将这些添加到计划的ROWCOUNT 0 版本中。

如果SELECT 定义了明确的TOPTOP 100 PERCENT 除外),那么 2012 年的计划中仍可能出现排序,但这仍然不能保证行的实际插入顺序,那么计划可能会有例如,在TOP N 之后建立另一种排序,以使行进入聚集索引顺序。

对于您问题中的示例,如果需要,我将调整调用代码以指定 ORDER BY name

关于来自Ordering guarantees in SQL Serversort_id 想法,保证在插入带有IDENTITY 的表时,这些分配的顺序将按照ORDER BY,所以你也可以这样做

DECLARE @Customer TABLE (
  Sort_Id     INT IDENTITY PRIMARY KEY,
  Customer_ID INT,
  Name        INT,
  Expired     BIT )

INSERT INTO @Customer
SELECT Customer_ID,
       Name,
       CASE
         WHEN Expiry_Date < Getdate() THEN 1
         WHEN Expired = 1 THEN 1
         ELSE 0
       END
FROM   Customer
ORDER  BY Name 

但您仍然需要在选择查询中按sort_id 进行排序,因为没有它就无法保证排序(也许这种sort_id 方法在用于排序的原始列不是的情况下可能很有用被复制到表变量中)

【讨论】:

  • +1 我一直认为ORDER BYINSERT 中被忽略。知道IDENTITY 列并非如此。
  • INSERT 并不总是遵守提供语句的顺序可以相当简单地显示:具有整数键和任意大行的表,以递增顺序单独插入许多行和聚集索引很整洁,以任意顺序单独插入,它会显示一些碎片。现在以递增和相同的任意顺序一次性插入相同的行(尝试使用SELECT TOP 100% ... ORDER BY 强制它):在这两种情况下,索引都是干净的,表明尽管在查询计划中看到了预期的排序操作,但忽略了任意顺序。
  • 实际上,如果您检查查询计划批量插入语句。您不需要那种排序,SQL Server 自己添加它以使插入索引更有效。请参阅pastebin.com/SVLtiRnP 以了解此操作的示例。
  • @DavidSpillett 希望答案this still doesn't guarantee actual insertion order of rows, the plan might then have another sort after the TOP N is established to get the rows into clustered index order for example 中已经提到了这一点。这个答案最近也从某个地方链接到了吗?我注意到最近对它的一些投票,这对于一个 7 岁的答案来说似乎是出乎意料的。
  • Brent Ozar 在一篇文章中提到了它。直到我添加了我的 cmets 之后,我才意识到这是一个较老的问题。
【解决方案2】:

将名为 rowno 的列添加到 @Customer 表

INSERT INTO @Customer

SELECT ROW_NUMBER()over(order by Name)rowno,Customer_ID, Name,
        CASE 
            WHEN Expiry_Date < GETDATE() then 1 
            WHEN Expired = 1 then 1 
            ELSE 0
            END
from Customer 

【讨论】:

  • 谢谢 - 我们将开始重新开发应用程序中的所有过程、函数和所有调用。谁说迁移很容易。
  • @TanisDraven - 你不需要使用ROW_NUMBER。您可以将IDENTITY 列添加到所有表中,并按外部选择中的值排序。见Ordering guarantees in SQL Server...。我还将IDENTITY 列设为聚集主键,以避免在SELECT 计划中进行排序(尽管您的外部查询仍然需要ORDER BY 才能获得保证排序)
猜你喜欢
  • 1970-01-01
  • 2020-07-10
  • 2020-07-09
  • 1970-01-01
  • 1970-01-01
  • 2018-04-24
  • 2013-03-14
  • 2011-05-02
  • 2018-05-13
相关资源
最近更新 更多