SQL 帮助 - 重写查询

Posted

技术标签:

【中文标题】SQL 帮助 - 重写查询【英文标题】:SQL Help- in rewriting a query 【发布时间】:2017-12-12 21:42:15 【问题描述】:

我们怎样才能减少下面查询的执行时间?

需要帮助以更有效的方式重写以下 SQL 查询?

SELECT A.*, C.*, F.*, D.*
FROM TABLE1 A  INNER JOIN
     TABLE2 C
     ON A.CODE = C.CODE INNER JOIN
     TABLE3 D
     ON A.CODE = D.CODE INNER JOIN
     TABLE4 F
     ON A.CODE = F.CODE
WHERE D.IND1 = 'N' AND
      D.IND2 = 'N' AND
      D.EFF_DATE = (SELECT MAX(X.EFF_DATE)
                    FROM TABLE3 X
                    WHERE X.CODE = D.CODE AND X.EFF_DATE <= A.EFFECTIVE_DATE
                   ) AND
      F.EFF_DATE = (SELECT MAX(Z.EFF_DATE)
                    FROM TABLE4 Z
                    WHERE Z.DETAIL_CODE = F.DETAIL_CODE AND Z.EFF_DATE <= A.EFFECTIVE_DATE
                   )

【问题讨论】:

我认为如果您提供示例数据集,您将获得更快、更有效的答案。通常,人们使用sqlfiddle.com 来提供这些集合。 你没有提供样本数据,没有执行计划,没有数据模型,没有解释业务规则,没有数据量或倾斜的指标,没有关于索引、统计等的信息。我们无法就重写此查询给您任何有意义的建议。 【参考方案1】:

为了性能,我会从以下索引开始:

TABLE3(IND1, IND2, CODE, EFF_DATE) TABLE3(CODE, EFF_DATE) TABLE1(CODE, EFF_DATE) TABLE2(CODE) TABLE4(CODE) TABLE4(DETAIL_CODE, EFF_DATE)

不过,如果您遇到性能问题,我怀疑您的代码可能会生成意外的笛卡尔积。需要更多信息的调试。我可能建议再问一个问题。

【讨论】:

【参考方案2】:

如果您能找出查询中的瓶颈在哪里——即子查询、连接——这将使您更好地了解要查看的内容。如果没有,请看一下:

修改列投影(即 A.、C.、F.、D.)以仅返回您需要的列 查看基于 DATE 值(TABLE3.EFF_DATE、TABLE4.EFF_DATE)访问行的查询的表分区 (http://www.oracle.com/technetwork/issue-archive/2006/06-sep/o56partition-090450.html) 考虑在整个查询或子查询中添加物化视图 (https://oracle-base.com/articles/misc/materialized-views) 如果查询计划不是最佳的,请查看统计信息生成 (https://docs.oracle.com/cd/A97630_01/server.920/a96533/stats.htm#26713)

如果您可以提供 EXPLAIN 计划(或 Oracle 的等效计划),那将很有帮助。

【讨论】:

【参考方案3】:

请注意,由于两个子查询的条件,结果中的所有记录都会有D.EFF_DATE &lt;= A.EFFECTIVE_DATEF.EFF_DATE &lt;= A.EFFECTIVE_DATE,所以我建议将这些条件放在JOIN 子句中。

其次,分析函数可能比子查询提供更好的性能:

SELECT *
FROM (
  SELECT      A.*,C.*,F.*,D.*,
              RANK() OVER (PARTITION BY D.CODE 
                           ORDER BY D.EFF_DATE DESC) AS D_RANK,
              RANK() OVER (PARTITION BY F.DETAIL_CODE 
                           ORDER BY F.EFF_DATE DESC) AS F_RANK
  FROM        TABLE1 A
  INNER JOIN  TABLE2 C
           ON A.CODE = C.CODE
  INNER JOIN  TABLE3 D
           ON A.CODE = D.CODE
          AND D.EFF_DATE <= A.EFFECTIVE_DATE
  INNER JOIN  TABLE4 F
           ON A.CODE = F.CODE
          AND F.EFF_DATE <= A.EFFECTIVE_DATE
  WHERE       D.IND1 = 'N'
          AND D.IND2 = 'N'
)
WHERE D_RANK = 1 AND F_RANK = 1

显然你需要有正确的索引来优化执行计划。

【讨论】:

感谢您的回复。如果我有多个代码和有效日期对(重复),如何使用排名将其限制为仅一条记录? 在这种情况下使用row_number() 而不是rank()。但是您的原始查询将对应于使用rank【参考方案4】:

要考虑的另一件事是查询返回的总列数,您似乎从 4 个表中选择了所有列。

我们发现,我们的复杂查询在仅选择几列时运行时间不到一秒,但在选择多列时需要更长的时间。

问为什么您的结果集中需要这么多列。

【讨论】:

以上是关于SQL 帮助 - 重写查询的主要内容,如果未能解决你的问题,请参考以下文章

在重写使用光标的查询时需要帮助

在 JOIN 中重写慢速 SQL(子)查询

用dataframe重写sql查询;如何从选择中混合不同的来源

重写 Oracle SQL 查询

SQL Server索引视图以(物化视图)及索引视图与查询重写

使用多个 UNION 重写 SQL Server 查询