我有一个非常复杂的存储过程,它根据传入的特定值重复一个非常复杂的查询,其中包含不同的where子句。存储过程占用了超过500行代码,查询的常见部分占用了100多个线。这个共同部分重复了3次。
我原本以为使用CTE(公用表表达式)除了在T-SQL中你不能定义公共部分,做你的IF
语句然后应用WHERE
子句。这基本上就是我的需要。
作为一种解决方法,我为公共代码创建了一个视图,但它只在一个存储过程中使用。
有没有办法在不创建完整视图或临时表的情况下执行此操作?
理想情况下,我想做这样的事情:
WITH SummaryCTE (col1, col2, col3...)
AS
(
SELECT concat("Pending Attachments - ", ifnull(countCol1, 0)) col1
-- all the rest of the stuff
FROM x as y
LEFT JOIN attachments z on z.attachmentId = x.attachmentId
-- and more complicated stuff
)
IF (@originatorId = @userId)
BEGIN
SELECT * FROM SummaryCTE
WHERE
-- handle this security case
END
ELSE IF (@anotherCondition = 1)
BEGIN
SELECT * FROM SummaryCTE
WHERE
-- a different where clause
END
ELSE
BEGIN
SELECT * FROM SummaryCTE
WHERE
-- the generic case
END
希望伪代码能让您了解我想要的内容。现在我的解决方法是为我定义的内容SummaryCTE
创建一个视图,然后处理IF
/ ELSE IF
/ ELSE
子句。执行此结构将在第一个IF
语句处抛出错误,因为下一个命令应该是SELECT
。至少在T-SQL中。
也许这不存在于任何其他方面,但我想确切地知道。
答案 0 :(得分:3)
那么,除了你已经识别的临时表和视图之外,你可以使用动态SQL来构建代码然后执行它。这使您不必重复代码,但使其有点难以处理。像这样:
declare @sql varchar(max) = 'with myCTE (col1, col2) as ( select * from myTable) select * from myCTE'
if (@myVar = 1)
begin
@sql = @sql + ' where col1 = 2'
end
else if (@myVar = 2)
begin
@sql = @sql + ' where col2 = 4'
end
-- ...
exec @sql
另一种选择是将不同的where子句合并到原始查询中。
WITH SummaryCTE (col1, col2, col3...)
AS
(
SELECT concat("Pending Attachments - ", ifnull(countCol1, 0)) col1
-- all the rest of the stuff
FROM x as y
LEFT JOIN attachments z on z.attachmentId = x.attachmentId
-- and more complicated stuff
)
select *
from SummaryCTE
where
(
-- this was your first if
@originatorId = @userId
and ( whatever you do for your security case )
)
or
(
-- this was your second branch
@anotherCondition = 1
and ( handle another case here )
)
or
-- etc. etc.
这消除了if / else链,但使查询更复杂。由于参数嗅探,它还可能导致一些错误的缓存查询计划,但这可能并不重要,具体取决于您的数据。在做出决定之前测试一下。 (您还可以添加优化程序提示以不缓存查询计划。您不会得到一个错误的提示,但您也会在每次执行时点击以再次创建查询计划。测试以查找,不要猜测。另外,带有视图的解决方案和if / else链将遭受相同的参数嗅探/缓存查询计划问题。)