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 <= A.EFFECTIVE_DATE
和F.EFF_DATE <= 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 帮助 - 重写查询的主要内容,如果未能解决你的问题,请参考以下文章
用dataframe重写sql查询;如何从选择中混合不同的来源