SQL Server 2005 中 CTE 的评估
我有一个关于 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 值传入的参数。由于上面使用值为 2 的
@NumEmployees
调用该函数,因此该函数的计算结果始终为 1 或 2。dbo.fnUtil_ParseCriteria(@CustomerIDs, 'int')
的结果生成一个单列单行表,其中包含基本类型为 'int' 的 sql_variant,该表具有值 219935。
鉴于上述假设,上面表达式的结果应该始终生成一个包含一条记录(CustomerID 和 EmployeeID)的两列表格(无论如何,对我来说)是有道理的。 CustomerID 应始终为 int 值 219935,EmployeeID 应为 42 或 43。
但是,情况并非总是如此。有时我会得到预期的单曲记录。有时我会得到两条记录(每个 EmployeeID 一条),还有一些时候我没有得到任何记录。但是,如果我用真正的临时表替换 RandomizedCustomers CTE,问题就会完全消失。
每当我认为我对这种行为有一个解释时,结果证明它没有意义或不可能,所以我实际上无法解释为什么会发生这种情况。由于当我用临时表替换 CTE 时不会发生问题,因此我只能假设它与在连接到该 CTE 期间评估 CTE 内的函数有关。你们有什么理论吗?
I have a question about how MS SQL evaluates functions inside CTEs. A couple of searches didn't turn up any results related to this issue, but I apologize if this is common knowledge and I'm just behind the curve. It wouldn't be the first time :-)
This query is a simplified (and obviously less dynamic) version of what I'm actually doing, but it does exhibit the problem I'm experiencing. It looks like this:
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;
The following can be assumed about all executions of the above:
The result of
dbo.fnUtil_Random()
is always an int value greater than zero and less than or equal to the argument passed in. Since it's being called above with@NumEmployees
which has the value 2, this function always evaluates to 1 or 2.The result of
dbo.fnUtil_ParseCriteria(@CustomerIDs, 'int')
produces a one-column, one-row table that contains a sql_variant with a base type of 'int' that has the value 219935.
Given the above assumptions, it makes sense (to me, anyway) that the result of the expression above should always produce a two-column table containing one record - CustomerID and an EmployeeID. The CustomerID should always be the int value 219935, and the EmployeeID should be either 42 or 43.
However, this is not always the case. Sometimes I get the expected single record. Other times I get two records (one for each EmployeeID), and still others I get no records. However, if I replace the RandomizedCustomers CTE with a true temp table, the problem vanishes completely.
Every time I think I have an explanation for this behavior, it turns out to not make sense or be impossible, so I literally cannot explain why this would happen. Since the problem does not happen when I replace the CTE with a temp table, I can only assume it has something to do with the functions inside CTEs are evaluated during joins to that CTE. Do any of you have any theories?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(1)
SQL Server
的优化器可以自由决定是否重新评估CTE
。例如,此查询:
将生成两个不同的
NEWID()
,但是,如果您使用缓存的XML
计划将CTE
包装到Eager Spool
操作,记录将是相同的。SQL Server
's optimizer is free to decide whether to reevaluate aCTE
or not.For instance, this query:
will produce two different
NEWID()
's, however, if you use cachedXML
plan to wrap theCTE
into anEager Spool
operation, the records will be same.