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内の関数が評価されることに関係があるとしか思えません。どなたか説はありますか?