以多种方式在 SQL 查询中多次使用复杂语句
Posted
技术标签:
【中文标题】以多种方式在 SQL 查询中多次使用复杂语句【英文标题】:Using complex statements in SQL queries multiple times in multiple ways 【发布时间】:2015-05-04 07:45:48 【问题描述】:我正在设计一个 Microsoft SQL Server 2008
上的 View
,它应该建立在相当多的业务逻辑之上,这意味着有很多 CASE WHEN THEN ELSE
语句。问题是,一个CASE
语句的结果通常在查询的其他地方需要,例如函数、连接和其他情况。这使得代码非常臃肿、难以阅读和维护。
以下是此类视图的示例(字段和功能仅是说明性的):
SELECT
-- Random complicated case
CASE
WHEN A=B AND CAST(C AS int)=D AND DATEDIFF(DAY,E,F) > 5
THEN D-A
WHEN A<>B AND CAST(C AS int)=D AND DATEDIFF(DAY,E,F) > 5
THEN D-A - 5
WHEN A=B AND CAST(C AS int)<>D AND DATEDIFF(DAY,E,F) > 5
THEN D-A - 10
WHEN A<>B AND CAST(C AS int)<>D AND DATEDIFF(DAY,E,F) > 5
THEN D-A - 15
END AS ComplicatedCase
-- Use of that same complicated case in another case
CASE
WHEN CASE
WHEN A=B AND CAST(C AS int)=D AND DATEDIFF(DAY,E,F) > 5
THEN D-A
WHEN A<>B AND CAST(C AS int)=D AND DATEDIFF(DAY,E,F) > 5
THEN D-A - 5
WHEN A=B AND CAST(C AS int)<>D AND DATEDIFF(DAY,E,F) > 5
THEN D-A - 10
WHEN A<>B AND CAST(C AS int)<>D AND DATEDIFF(DAY,E,F) > 5
THEN D-A - 15
END > 300
THEN CASE
WHEN A=B AND CAST(C AS int)=D AND DATEDIFF(DAY,E,F) > 5
THEN D-A
WHEN A<>B AND CAST(C AS int)=D AND DATEDIFF(DAY,E,F) > 5
THEN D-A - 5
WHEN A=B AND CAST(C AS int)<>D AND DATEDIFF(DAY,E,F) > 5
THEN D-A - 10
WHEN A<>B AND CAST(C AS int)<>D AND DATEDIFF(DAY,E,F) > 5
THEN D-A - 15
END
ELSE NULL
END AS AnotherCase
FROM SomeTable AS T
-- Complicated case in join
INNER JOIN AnotherTable AS AT
ON AT.ID = CASE
WHEN A=B AND CAST(C AS int)=D AND DATEDIFF(DAY,E,F) > 5
THEN D-A
WHEN A<>B AND CAST(C AS int)=D AND DATEDIFF(DAY,E,F) > 5
THEN D-A - 5
WHEN A=B AND CAST(C AS int)<>D AND DATEDIFF(DAY,E,F) > 5
THEN D-A - 10
WHEN A<>B AND CAST(C AS int)<>D AND DATEDIFF(DAY,E,F) > 5
THEN D-A - 15
END
代码很长,不可读,如果需要更改业务逻辑,我更有可能忘记更改。
我想到的明显解决方案是在子选择中添加案例,通过INNER JOIN
或CROSS APPLY
加入,如下所示:
-- Solution by CROSS APPLY sub-select
SELECT
T1.ComplicatedCase,
CASE
WHEN ComplicatedCase > 300
THEN ComplicatedCase
ELSE NULL
END AS AnotherCase
FROM SomeTable AS T
CROSS APPLY
(
SELECT
CASE
WHEN A=B AND CAST(C AS int)=D AND DATEDIFF(DAY,E,F) > 5
THEN D-A
WHEN A<>B AND CAST(C AS int)=D AND DATEDIFF(DAY,E,F) > 5
THEN D-A - 5
WHEN A=B AND CAST(C AS int)<>D AND DATEDIFF(DAY,E,F) > 5
THEN D-A - 10
WHEN A<>B AND CAST(C AS int)<>D AND DATEDIFF(DAY,E,F) > 5
THEN D-A - 15
END AS ComplicatedCase
FROM SomeTable AS ST
WHERE T.ID = ST.ID
) AS T1
INNER JOIN AnotherTable AS AT ON AT.ID = T1.ID
...而且效果很好,但我不知道查询性能是否不会大规模受到影响(因为我不太了解引擎如何在内部处理这些东西)。
对于如何处理此类复杂语句,您有任何最佳实践吗?如果它在一个简单的连接子选择中,它甚至对 SQL 引擎有影响吗?
PS:我的case语句通常由几个函数组成,主要是datetime
的处理和转换。
【问题讨论】:
【参考方案1】:我认为你不必CROSS APPLY
你的SomeTable
两次。
这应该可行:
SELECT T1.ComplicatedCase
, CASE
WHEN ComplicatedCase > 300 THEN ComplicatedCase
ELSE NULL
END AS AnotherCase
FROM SomeTable AS T
CROSS APPLY (
SELECT DATEDIFF(DAY, T.E, T.F)
) AS TT(DayDiff)
CROSS APPLY (
SELECT CASE
WHEN T.A = T.B AND CAST(T.C AS INT) = T.D AND TT.DayDiff > 5 THEN D - A
WHEN T.A <> T.B AND CAST(T.C AS INT) = T.D AND TT.DayDiff > 5 THEN D - A - 5
WHEN T.A = T.B AND CAST(T.C AS INT) <> T.D AND TT.DayDiff > 5 THEN D - A - 10
WHEN T.A <> T.B AND CAST(T.C AS INT) <> T.D AND TT.DayDiff > 5 THEN D - A - 15
END
) AS T1(ComplicatedCase)
INNER JOIN AnotherTable AS AT
ON AT.ID = T1.ComplicatedCase;
CROSS APPLY
允许您创建计算值并在JOINS
、WHERE
语句等中使用它们。它使代码更具可读性,并且不应该为您花费额外的资源。
如果有什么不清楚或不符合您的标准 - 请告诉我。
【讨论】:
嗨@Evaldas,除了正确的表引用之外,我认为您的解决方案和我的解决方案没有太大区别。但我喜欢只为 DATEDIFF 使用 CROSS APPLY 的想法。这对 SQL 引擎有影响还是只是为了可读性?另外,将别名分配给带括号的子选择是什么意思?像这样:AS TT(DayDiff)...我以前没见过这个。谢谢! 嘿@MarekStejskal,我看到了不同之处,您在不需要时在SomeTable
之间进行了两次引用(至少对我来说看起来是这样),这是额外费用,请参见此处:prntscr.com/71csl1。它不应该对 SQL Server 引擎产生任何显着影响,它只是保持可读性。对于您的第二个问题 - 这只是分配别名的另一种方式。我习惯于为CROSS APPLY
或OUTER APPLY
这样做。以上是关于以多种方式在 SQL 查询中多次使用复杂语句的主要内容,如果未能解决你的问题,请参考以下文章