4

MS SQL が CTE 内の関数を評価する方法について質問があります。いくつかの検索では、この問題に関連する結果は得られませんでしたが、これが一般的な知識であり、私が時代遅れである場合はお詫び申し上げます. 初めてではないでしょう:-)

このクエリは、私が実際に行っていることの単純化された (そして明らかに動的ではない) バージョンですが、私が経験している問題を示しています。次のようになります。

CREATE TABLE #EmployeePool(EmployeeID int, EmployeeRank int);

INSERT INTO #EmployeePool(EmployeeID, EmployeeRank)
  SELECT 42, 1
  UNION ALL 
  SELECT 43, 2;

DECLARE @NumEmployees int;
SELECT @NumEmployees  = COUNT(*) FROM #EmployeePool;

WITH RandomizedCustomers AS (
  SELECT CAST(c.Criteria AS int) AS CustomerID,
         dbo.fnUtil_Random(@NumEmployees) AS RandomRank
    FROM dbo.fnUtil_ParseCriteria(@CustomerIDs, 'int') c)
SELECT rc.CustomerID,
       ep.EmployeeID
  FROM RandomizedCustomers rc
  JOIN #EmployeePool ep ON ep.EmployeeRank = rc.RandomRank;

DROP TABLE #EmployeePool;

上記のすべての実行について、次のことが想定できます。

  • の結果は、dbo.fnUtil_Random()常に 0 より大きく、渡された引数以下の int 値です。上記で呼び出され@NumEmployeesているため、値 2 を持つため、この関数は常に 1 または 2 に評価されます。

  • の結果dbo.fnUtil_ParseCriteria(@CustomerIDs, 'int')は、値が 219935 の「int」の基本型を持つ sql_variant を含む 1 列、1 行のテーブルを生成します。

上記の仮定を考えると、上記の式の結果が常に 1 つのレコード (CustomerID と EmployeeID) を含む 2 列のテーブルを生成する必要があることは (とにかく私にとっては) 理にかなっています。CustomerID は常に int 値 219935 である必要があり、EmployeeID は 42 または 43 のいずれかである必要があります。

ただし、常にそうであるとは限りません。期待される単一のレコードを取得することがあります。2 つのレコード (EmployeeID ごとに 1 つ) を取得する場合もあれば、レコードを取得しない場合もあります。ただし、RandomizedCustomers CTE を真の一時テーブルに置き換えると、問題は完全に解消されます。

この行動について説明できると思うたびに、それは意味をなさないか不可能であることが判明するので、なぜこれが起こるのかを文字通り説明することはできません. CTEを一時テーブルに置き換えても問題は発生しないため、CTEへの結合中にCTE内の関数が評価されることに関係があるとしか思えません。どなたか説はありますか?

4

1 に答える 1

4

SQL Serverのオプティマイザは、 a を再評価するかどうかを自由に決定CTEできます。

たとえば、次のクエリは次のようになります。

WITH    q AS
        (
        SELECT  NEWID() AS n
        )
SELECT  *
FROM    q
UNION ALL
SELECT  *
FROM    q

は 2 つの異なる を生成しNEWID()ますが、キャッシュされたXMLプランを使用して を操作にラップするCTEEager Spool、レコードは同じになります。

于 2010-03-26T14:51:56.997 に答える