24

我们正在从 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 中,它返回未排序的表。SQL 2012 中忽略了“ order by” 。

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

4

2 回答 2

37

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

  1. 在插入表时,永远不能保证ORDER BYonINSERT ... SELECT ... ORDER BY将是实际插入行的顺序。
  2. 在从中选择时,SQL Server 不保证SELECT没有 anORDER BY将返回任何特定顺序的行,例如插入顺序。

在 2012 年,第 1 项的行为似乎发生了变化。现在它通常ORDER BY忽略SELECT作为INSERT

DECLARE @T TABLE(number int)

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

2008年计划

2008年计划

2012年计划

2012年计划

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

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

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

对于您问题中的示例,我只需调整调用代码以指定ORDER BY name它是否需要。

关于您在 SQL Server 中sort_id的订购保证中的想法,可以保证在插入表时,这些表的分配顺序将按照,因此您也可以这样做IDENTITYORDER 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方法在用于排序的原始列没有被复制到表变量中的情况下可能很有用)

于 2012-06-27T17:34:45.697 回答
3

将名为 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 
于 2012-06-27T08:29:18.363 回答