SQL IF-ELSE结构中的CTE

6
我想要做一些类似这样的事情。
declare @a int=1
if (@a=1)
with cte as 
(
select UserEmail from UserTable 
)
else
with cte as
(
select UserID from UserTable
)
select * from cte

这只是一个例子,我的实际查询要复杂得多。因此,我不想在CTE之后两次写IFELSE语句中的SELECT语句。

3个回答

14
如果可能的话,找到一种避免使用if语句的方法。
例如,对于像您问题中提供的如此微不足道的示例:
;with CTE as (
      select UserEmail from UserTable where @a = 1
      union all
      select UserID from UserTable where @a != 1 or @a is null
)
select /* single select statement here */

通常情况下,可以将一个或多个不同的查询组合成最终的UNION ALL cte,而不是使用if - 毕竟,被组合的两个查询结果集必须兼容,才能使你的原始问题有意义。


一个非常好的解决方法来自Damien,使用'Union ALL'并在where子句中添加'if'条件过滤器。为了提高性能,我建议将过滤器作为'Where'子句中的第一条语句。 - Muhammad Omar ElShourbagy
@Damien_The_Unbeliever,您能详细说明一下为什么我们应该避免使用“IF”吗? - Nikita Silverstruk
1
@NikitaSilverstruk - 这不是绝对禁止,更多的是一般性指导 - 如果您正在尝试生成一个结果集,应该尝试在单个查询中制定如何生成该结果集,并让优化器找出最佳方法来生成它,而不是手动将该查询分成块。正如您可以从marc_s的答案中看到的那样,使用if的替代方法最终会产生两个具有重复内容的查询。 - Damien_The_Unbeliever

6
你不能这样做 - CTE必须紧接着一个能够引用它的SQL语句。你不能将CTE的“定义”与使用它的语句分开。
因此,你需要按照如下方式操作:
declare @a int=1

if (@a=1)
    with cte as 
    (
       select UserEmail from UserTable 
    )
    select * from cte

else

    with cte as
    (
       select UserID from UserTable
    )
    select * from cte

您不能将CTE的“定义”用于其用法(select * from cte)中拆分


@MartinSmith:我就怕这个(但是在文档中找不到明确的说明):-( 因此我删除了我的部分。感谢您指出! - marc_s
我知道CTE(公用表达式)必须紧随一条SQL语句之后。但是我正在寻找绕过的方法。我不想编写重复的SELECT语句。 - Manvinder
1
@MegaMind:无论如何也绕不过去-这就是CTE定义的工作方式。 - marc_s

4
"with cte (...) select from cte ...是一个单独的语句。它不是紧随其后的语句,而是语句的一部分。您要求将语句拆分为两个部分,这显然是不可能的。
通常情况下,SQL对于像DRY和避免代码重复之类的事情来说是一种非常不友好的语言。试图使代码更易于维护、更易于阅读或仅仅试图节省几个按键可能会(并且通常会)导致严重的运行时性能损失(例如,尝试将CTE移入表值函数UDF)。最简单的方法是咬紧牙关(这次和将来...)并将CTE写两次。有时将CTE实现为#temp表,然后在#temp表上操作是有意义的,但只有在某些情况下。
status-quo is unfortunate,但这就是您可以从design by committee期望的一切..."

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接