4

我有一个关于MS SQL如何评估CTE内部函数的问题。一些搜索没有得出任何有关这个问题的结果,但我很抱歉,如果这是常识,我只是在曲线后面。这不会是第一次:-)评估SQL Server 2005中的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')结果产生一列,一列表,包含3纳秒与碱型,其具有的值219935.

鉴于上述假设“INT”的SQL_VARIANT,是有意义(我,反正)该表达式的结果上述应该总是产生两包含一条记录的列表 - CustomerID和一个EmployeeID。 CustomerID应始终为int值219935,并且EmployeeID应为42或43.

但是,情况并非总是如此。有时我会得到预期的单一记录。其他时候,我得到两个记录(每个EmployeeID一个),还有一些我没有记录。但是,如果我将RandomizedCustomers CTE替换为真正的临时表,则问题完全消失。

每次我想我对这种行为有一个解释,结果是没有意义或不可能,所以我实际上无法解释为什么会发生这种情况。由于当我使用临时表替换CTE时,问题不会发生,因此我只能假定它与CTE内部的函数在连接到CTE期间进行评估。你们有没有任何理论?

回答

3

SQL Server的优化器可以自由决定是否重新评估CTE或不。

例如,这个查询:

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

会产生两种不同的NEWID()的,但是,如果你使用缓存XML计划包裹CTEEager Spool操作,这些记录将是相同的。

+0

我现在完全明白了。非常感谢您的帮助! – Jammer 2010-03-26 15:46:32