在 SQL 中使用复杂语句以多种方式多次查询

Using complex statements in SQL queries multiple times in multiple ways

我正在 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

代码很长,难以阅读,如果需要更改业务逻辑,我很可能会忘记更改。

我想到的明显解决方案是在子 select 中处理案例,或者由 INNER JOINCROSS 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 引擎处于简单连接的子 select 中,它对 SQL 引擎有影响吗?

PS:我的 case 语句通常由几个函数组成,主要是 datetime 处理和转换。

我认为您不必 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 允许您创建计算值并在 JOINSWHERE 语句等中使用它们。它使代码更具可读性,并且不会为您花费额外的资源。

如果有什么不清楚或不符合您的标准 - 让我知道。