#1 楼
看来它们并没有,但实际上仅适用于嵌套的CTE。创建两个临时表:
CREATE TABLE #t1 (id INT);
INSERT #t1 ( id )
VALUES ( 1 );
CREATE TABLE #t2 (id INT);
INSERT #t2 ( id )
VALUES ( 1 );
查询1 :
WITH your_mom AS (
SELECT TOP 1 *
FROM #t1 AS t
),
also_your_mom AS (
SELECT TOP 1 *
FROM #t2 AS t
)
SELECT *
FROM your_mom;
查询2:
WITH your_mom AS (
SELECT TOP 1 *
FROM #t1 AS t
),
also_your_mom AS (
SELECT TOP 1 *
FROM #t2 AS t
)
SELECT *
FROM also_your_mom;
查询计划:
有开销,但是查询的不必要部分很早就消除了(在这种情况下为解析;在更复杂的情况下为简化阶段),因此,额外的工作实际上是最少的,并且确实不会有助于潜在的昂贵的基于成本的优化。
#2 楼
为Erik +1,但想添加两件事(在注释中效果不佳):您甚至无需查看执行计划即可看到它们在不使用时被忽略。以下应产生“被0除”错误,但不是由于根本没有选择
cte2
:;WITH cte1 AS
(
SELECT 1 AS [Bob]
),
cte2 AS (
SELECT 1 / 0 AS [Err]
FROM cte1
)
SELECT *
FROM cte1;
CTE可以忽略,即使它们是唯一的CTE,即使从中选择它们,从逻辑上来说,所有行都将被排除。以下是查询优化器提前知道CTE不能返回任何行的情况,因此它甚至不必费心执行它:
;WITH cte AS
(
SELECT 1 / 0 AS [Bob]
)
SELECT TOP (1) [object_id]
FROM sys.objects
UNION ALL
SELECT cte.[Bob]
FROM cte
WHERE 1 = 0;
关于性能,对未使用的CTE进行了解析和编译(或至少在以下情况下进行了编译),因此不能100%忽略它,但是成本必须可以忽略不计,也不值得关注。 br />
仅分析时,没有错误:
SET PARSEONLY ON;
;WITH cte1 AS
(
SELECT obj.[NotHere]
FROM sys.objects obj
)
SELECT TOP (1) so.[name]
FROM sys.objects so
GO
SET PARSEONLY OFF;
GO
当执行仅欠执行时的所有操作时,就会出现问题:
GO
SET NOEXEC ON;
GO
;WITH cte1 AS
(
SELECT obj.[NotHere]
FROM sys.objects obj
)
SELECT TOP (1) so.[name]
FROM sys.objects so
GO
SET NOEXEC OFF;
GO
/*
Msg 207, Level 16, State 1, Line XXXXX
Invalid column name 'NotHere'.
*/
评论
希望我可以将多个答案标记为正确答案,但是Erik击败了您,获得了手枪抽奖。 :)但是您的答案也非常有用,非常感谢,谢谢!
– J.D.
19-3-2在2:25
如果CTE在视图中并且视图嵌套3次以上怎么办?优化器不会放弃并全部运行吗?
– Zikato
19年4月10日在13:20
@Zikato我不知道,但这是一个很好的问题。通过使用我在前两个示例中展示的“除以零”技巧创建视图,您应该能够轻松地建立测试。请让我知道结果,因为我现在对这种情况非常好奇:-)。
–所罗门·鲁兹基
19年4月10日在14:21
@SolomonRutzky公平地说,我确实对其进行了测试,但这并不是结论性的。我已经从您的cte示例中创建了一个视图并将其嵌套5次,但是由于它都是恒定扫描并且并不复杂,因此优化程序可以很好地处理它。我希望将来对其进行更全面的测试,并将其隐藏在更复杂的逻辑中。我会告诉你。
– Zikato
19年4月10日在14:26
@Zikato有趣。不知道什么会被认为是“复杂的”,但是是的,我的例子非常简单。当您说“嵌套5次”时,您是说在其他相互调用且深度为5的视图/过程中还是在子查询/ CTE中?我认为有可能嵌套足够多的级别可能会跳过它,但这不是由于未引用它,而是由于较高的嵌套级别未使用它,而假定是针对较低的级别。我已经看到了将NEWID()放入UDF视图中的窍门,由于优化程序将其缓存,因此可以从多个调用返回相同的值。
–所罗门·鲁兹基
19年4月10日在14:49