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

标签 sql-server sql-server-2008 t-sql query-optimization

我正在 Microsoft SQL Server 2008 上设计一个View,它应该构建在相当多的业务逻辑之上,这意味着有很多CASE WHEN THEN ELSE 语句。问题是,查询中的其他地方通常需要一个 CASE 语句的结果,例如函数、联接和其他情况。这使得代码变得极其臃肿,难以阅读和维护。

下面是此类 View 的示例(字段和函数仅用于说明):

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 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 引擎位于简单的连接子选择中,这对 SQL 引擎来说还重要吗?

PS:我的 case 语句通常由多个函数组成,主要是日期时间处理和转换。

最佳答案

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

如果有些内容不清楚或不符合您的标准 - 请告诉我。

关于sql-server - 以多种方式在 SQL 查询中多次使用复杂语句,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/30025067/

相关文章:

sql - 批量删除行数据

sql - 从查询 sql 中使用的 MAX() 生成的结果中删除空值

SQL Management Studio 2008 - 查询编辑器选项卡上的服务器名称...它们去了哪里?

SQL Server : if "datetime2" < 1753, 然后 "datetime"NULL?

sql-server - 如何从 ruby​​ 连接到 MS SQL Server

sql-server - 划分结果以运行总计查询

sql - 使用 XML 数据类型读取存储在 SQL Server 2008 R2 中的 XML 文档

mysql - 存储过程中的多案例选择

java - SQL异常仅发生在三台服务器之一上

c# - 以编程方式分离数据库