web-dev-qa-db-ja.com

テーブル値関数-orderbyは出力で無視されます

SQL Server2008からSQLServer 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"はSQL2012では無視されます。

すべての関数を書き直してsort_idを含め、メインアプリケーションで呼び出されたときに並べ替える必要がありますか、それとも簡単な修正がありますか?

24
Tanis Draven

元のアプローチには2つの問題がありました。

  1. テーブルに挿入するときに、_ORDER BY_の_INSERT ... SELECT ... ORDER BY_が実際に行が挿入された順序であることが保証されることはありませんでした。
  2. SQL Serverは、そこから選択しても、_ORDER BY_のないSELECTが挿入順序などの特定の順序で行を返すことを保証しません。

2012年には、項目1に関して動作が変更されたように見えます。現在は通常、SELECTのソースであるINSERTステートメントの_ORDER BY_を無視します。

_DECLARE @T TABLE(number int)

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

2008年計画

2008 plan

2012年計画

2012 plan

動作が変更された理由は、以前のバージョンでは、SQL Serverが_SET ROWCOUNT 0_(オフ)と_SET ROWCOUNT N_の実行間で共有される1つのプランを作成したためです。ソート演算子は、プランがゼロ以外のROWCOUNTセットのセッションによって実行された場合に正しいセマンティクスを保証するためにのみ存在していました。その左側のTOP演算子は _ROWCOUNT TOP_ です。

SQL Server 2012は、2つのケースに対して別々のプランを作成するようになったため、これらをプランの_ROWCOUNT 0_バージョンに追加する必要はありません。

SELECTに明示的なTOPが定義されている場合(_TOP 100 PERCENT_以外)、2012年のプランに並べ替えが表示される場合がありますが、これは行の実際の挿入順序を保証するものではありません。たとえば、行をクラスター化されたインデックスの順序にするために_TOP N_が確立された後、プランは別の並べ替えを行う場合があります。

あなたの質問の例では、それが必要な場合は、呼び出しコードを調整して_ORDER BY name_を指定します。

SQL Server での順序保証からの_sort_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_アプローチは、順序付けに使用された元の列がない場合に役立つ可能性があります。 tはテーブル変数にコピーされます)

34
Martin Smith

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 
3
Nighil