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()始终是大于零且小于或等于传入参数的 int 值。由于在上面调用它时@NumEmployees其值为 2,因此此函数的计算结果始终为 1 或 2。

  • 的结果dbo.fnUtil_ParseCriteria(@CustomerIDs, 'int')生成一个单列单行表,其中包含一个 sql_variant,其基类型为“int”,值为 219935。

鉴于上述假设,(无论如何对我而言)上述表达式的结果应该始终生成一个包含一条记录的两列表 - CustomerID 和 EmployeeID。CustomerID 应始终为 int 值 219935,而 EmployeeID 应为 42 或 43。

然而,这并非总是如此。有时我会得到预期的单曲记录。其他时候我得到两条记录(每个 EmployeeID 一条),还有一些我没有得到任何记录。但是,如果我用真正的临时表替换 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

将产生两个不同NEWID()的 ',但是,如果您使用缓存XML计划将 包装CTE到一个Eager Spool操作中,则记录将是相同的。

于 2010-03-26T14:51:56.997 回答