【问题标题】:Optimizing SQL query with a single join使用单个连接优化 SQL 查询
【发布时间】:2019-11-23 15:43:55
【问题描述】:

我需要帮助优化我的查询,因为我们的 SAP HANA 环境在尝试运行以下命令时一直失败。

我只是想获取年初至今的发票数据以及发票是否打开。我们存储发票行及其状态(打开/关闭),不幸的是,我们本身不存储发票编号,但发票编号存储在两个字段之一中,具体取决于“Sales_Document_Type”。

因此,我编写了以下查询来检索所有发票、一个 CASE 语句标志来识别打开/关闭和最后一个 Clearing_Date。我相当肯定这会起作用,但我正在寻求帮助,建议优化它的方法,因为它无法以当前形式处理。

SELECT
    CASE
        WHEN A."Sales_Document_Type" IN ('A','B','C') THEN A."Accounting_Document_No"
    ELSE A."Assignment_Number" END AS "Invoice_Number",
    CASE
        WHEN A."Sales_Document_Type" IN ('A','B','C') THEN (
        CASE
            WHEN B."Accounting_Document_No" IS NULL THEN 'C'
        ELSE 'O' END
        )
    ELSE (
        CASE
            WHEN B."Accounting_Document_No" IS NULL THEN 'C'
        ELSE 'O' END
        ) 
    END AS "Open_Close_Flag",
     MAX(A."Clearing_Date") AS "Clearing_Date"

FROM 
    "AccountsReceivable" A

LEFT JOIN 
    "AccountsReceivable" B ON (A."Assignment_Number" = B."Assignment_Number" OR A."Accounting_Document_No" = B."Accounting_Document_No") AND B."OpenClose_Flag" = 'O' AND B."Creation_Date" >= '2019-01-01'

WHERE
    A."Creation_Date" >= '2019-01-01'

GROUP BY
    CASE
        WHEN A."Sales_Document_Type" IN ('A','B','C') THEN A."Accounting_Document_No"
    ELSE A."Assignment_Number" END,
    CASE
        WHEN A."Sales_Document_Type" IN ('A','B','C') THEN (
        CASE
            WHEN B."Accounting_Document_No" IS NULL THEN 'C' 
        ELSE 'O' END
        )
    ELSE (
        CASE
            WHEN B."Accounting_Document_No" IS NULL THEN 'C'
        ELSE 'O' END
        ) 
    END

“AccountsReceivable”表中的示例数据如下所示(标签缩写):

Sales_Doc_Type | Accounting_Doc_# | Assignment_# | Line | OpenClose_F | Clearing_Date
A              | 1234             | 5678         | 1    | O           | 
A              | 1234             | 5678         | 2    | C           | 2019-10-01
C              | 1235             | 5679         | 1    | O           | 
D              | 1001             | 9876         | 1    | O           | 
D              | 1001             | 9876         | 2    | C           | 2019-10-01
D              | 1002             | 9870         | 1    | C           | 2019-10-05
D              | 1002             | 9870         | 2    | C           | 2019-10-09

需要注意的是,文档有多行,每行可以是打开的(OpenClose_Flag of O)或关闭的(C)。我想要的输出是 Accounting_Document_No 或 Assignment_Number AS Invoice_Number(取决于 sales_document_type),如果任何行都是 O,则为 O,如果所有行均为 C,则为 C,最大清算日期:

Invoice_Number | OpenClose_Flag | MAX(Clearing_Date)
1234           | O              | 2019-10-01
1235           | O              |
9876           | O              | 2019-10-01
9870           | C              | 2019-10-09

非常感谢您的帮助!

【问题讨论】:

  • 请描述您要实现的逻辑。样本数据和期望的结果会有所帮助。如果没有样本数据,几乎无法理解围绕“发票”的逻辑。
  • 好点,我刚刚编辑了原始帖子以包含一些示例数据和所需的结果。

标签: sql sap hana


【解决方案1】:

如果您确定查询产生了您正在寻找的结果,您需要查看查询执行计划并在那里进行优化。

有时需要寻址的不是查询而是表。

这个特定的查询很大但很乏味:

SELECT (SOME BASIC STUFF)
FROM Table1 A 
    LEFT JOIN Table1 B on (JOIN CONDITION)
WHERE
    (FILTER CONDITION)
GROUP BY (FOUND IN SELECT)

优化执行计划:

  • 确保您的过滤条件已编入索引。
  • 确保您的联接条件已编入索引(而不是部分联接)

您的联接中的 OR 条件使得创建理想索引变得困难,请使用下面的两个作为指导来查看系统选择和删除另一个。

CREATE INDEX IDX_AccountsReceivable_Creation_Date_OpenClose_Flag_Accounting_Document_No ON AccountsReceivable (Creation_Date, OpenClose_Flag, Accounting_Document_No)
CREATE INDEX IDX_AccountsReceivable_Accounting_Creation_Date_OpenClose_Flag_Assignment_Number ON AccountsReceivable (Creation_Date, OpenClose_Flag, Assignment_Number)

如果您使用的是数据子集,请确保您的条件正在触发您的索引,而不是像这样进行表/索引扫描:

【讨论】:

  • 这些技巧可能适用于 MS SQL,但对于 HANA 附加索引 - 尤其是多列索引 - 很少是最佳解决方案。
  • 愚蠢的我,我错误地认为这是一个在 SQL Server 之上工作的 BI 工具,结果它是它自己的 dbms。索引是一个非常通用的数据库概念。如果它在这里不起作用,那就太糟糕了。
  • 不幸的是,在这种情况下,我是数据的低级消费者,无法更改表、索引字段等的结构。虽然我什至不知道哪些字段被索引怀疑 Creation_Date 是......至少对于这个特定的表来说,它是一个要索引的逻辑字段。
【解决方案2】:

首先:要确定这是否真的会提高性能,需要合理数量的测试数据。通过提供的少数示例行,我们所能做的就是查看结果是否符合预期,但运行时差异通常在运行时可变性的边缘。

这里有免责声明,我的回答方法。

原始语句的主要问题是它使用了难以并行运行的构造,并且难以在记录集上运行。 CASE 表达式需要针对每一行计算,JOIN 会在处理数据流中强制设置一个同步点。

为了避免这两种情况,我将 CASE 构造替换为将不同的 "Invoice_Number" 编码分成子集,然后将它们 UNION 编在一起。 基于此,我确定没有未结发票的“Invoice_Numbers”,即这些组被视为"C"losed。

然后将所有剩余的组视为"O"pen。

最后的语句如下所示:

with base as (
    SELECT
         "Assignment_#"          AS "Invoice_Number"
       , "OpenClose_F"
       , "Clearing_Date"
       , "Creation_date"
    FROM 
        "AccountsReceivable" 
    WHERE
         "Sales_Doc_Type" NOT IN ('A','B','C')
      and "Creation_date" >= '2019-01-01'
UNION ALL 
    SELECT
         "Accounting_Doc_#"          AS "Invoice_Number"
       , "OpenClose_F"
       , "Clearing_Date"
       , "Creation_date"
    FROM 
        "AccountsReceivable" 
    WHERE
         "Sales_Doc_Type" IN ('A','B','C')
      AND "Creation_date" >= '2019-01-01' ),
closed as (
            select 
                 "Invoice_Number"
               , 'C' as "OpenClose_F"
               , max("Clearing_Date") as max_clearing_date
            from base b
            where NOT EXISTS (SELECT *
                             FROM base
                             where "Invoice_Number" = b."Invoice_Number"
                             and "OpenClose_F" = 'O')
            group by "Invoice_Number"
                     , 'C'),
open as (
        select 
             "Invoice_Number"
           , 'O' as "OpenClose_F"
           , max("Clearing_Date") as max_clearing_date
        from base b
        where NOT EXISTS (SELECT *
                         FROM closed
                         where "Invoice_Number" = b."Invoice_Number")
        group by "Invoice_Number"
                 , 'O')

select 'O' as grp
     , "Invoice_Number"
     , "OpenClose_F"
     , max_clearing_date
from open
union all
select 'C' as grp
     , "Invoice_Number"
     , "OpenClose_F"
     , max_clearing_date
from closed

如果数据允许,这种方法还允许通过索引和分区进行额外的调整。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2012-09-26
    • 1970-01-01
    • 2020-08-08
    • 2016-10-16
    • 1970-01-01
    • 1970-01-01
    • 2017-04-24
    • 2018-12-27
    相关资源
    最近更新 更多