在 Oracle 中避免相关子查询
Posted
技术标签:
【中文标题】在 Oracle 中避免相关子查询【英文标题】:Avoiding Correlated Subquery in Oracle 【发布时间】:2012-05-23 15:13:45 【问题描述】:在 Oracle 9.2.0.8 中,我需要返回一个记录集,其中每个字段的特定字段 (LAB_SEQ) 最大(它是顺序 VARCHAR 数组 '0001'、'0002' 等) (WO_NUM)。要选择最大值,我尝试按降序排序并选择第一行。我在 *** 上可以找到的所有内容都表明,唯一的方法是使用相关子查询。然后我在外部查询的 WHERE 子句中使用这个最大值来为每个 WO_NUM 获取我想要的行:
SELECT lt.WO_NUM, lt.EMP_NUM, lt.LAB_END_DATE, lt.LAB_END_TIME
FROM LAB_TIM lt WHERE lt.LAB_SEQ = (
SELECT LAB_SEQ FROM (
SELECT lab.LAB_SEQ FROM LAB_TIM lab WHERE lab.CCN='1' AND MAS_LOC='1'
AND lt.WO_NUM = lab.WO_NUM ORDER BY ROWNUM DESC
) WHERE ROWNUM=1
)
但是,这会为 lt.WO_NUM 错误返回无效标识符。研究表明,ORacle 8 只允许关联子查询更深一层,并建议重写以避免子查询——选择最大值的讨论表明无法做到这一点。任何帮助执行此语句将不胜感激。
【问题讨论】:
按 ROWNUM 排序在这里并不是很有用。 【参考方案1】:您的相关子查询需要类似于
SELECT lt.WO_NUM, lt.EMP_NUM, lt.LAB_END_DATE, lt.LAB_END_TIME
FROM LAB_TIM lt WHERE lt.LAB_SEQ = (
SELECT max(lab.LAB_SEQ)
FROM LAB_TIM lab
WHERE lab.CCN='1' AND MAS_LOC='1'
AND lt.WO_NUM = lab.WO_NUM
)
由于您使用的是 Oracle 9.2,因此使用相关子查询可能会更有效。我不确定谓词 lab.CCN='1' AND MAS_LOC='1'
在您当前的查询中做了什么,所以我不太确定如何将它们转换为分析函数方法。 LAB_SEQ
和 WO_NUM
的组合在 LAB_TIM
中不是唯一的吗?您是否需要在CCN
和MAS_LOC
上添加谓词才能为每个WO_NUM
获得一个唯一行?或者您是否使用这些谓词来减少输出中的行数?基本方法类似于
SELECT *
FROM (SELECT lt.WO_NUM,
lt.EMP_NUM,
lt.LAB_END_DATE,
lt.LAB_END_TIME,
rank() over (partition by wo_num
order by lab_seq desc) rnk
FROM LAB_TIM lt)
WHERE rnk = 1
但我不清楚CCN
和MAS_LOC
是否需要添加到解析函数的ORDER BY
子句中,或者是否需要添加到WHERE
子句中。
【讨论】:
+1,虽然 8i 已经有 analytics :) @VincentMalgrat - 你是对的 - 看起来这是在 8.1.6 中引入的。 谢谢,贾斯汀。虽然这实现了我的目标,但它的效率非常低。由于 max() 函数将遍历表中的每个条目(很大),我想利用行实际上是按顺序排列的事实。我认为按 ROWNUM 排序会比 LAB_SEQ 更有效,因为比较字符串很耗时。您建议的查询现在已经运行了大约 5 分钟,看不到尽头。 @user1412922 - 在您提供ORDER BY
子句之前,堆组织表中的行本质上是无序的。按ROWNUM
排序相当于省略ORDER BY
子句,并将以任意顺序返回数据。在非常受控的环境中(永远不会删除或更新数据,一次只有一个会话插入数据,永远不会导出数据,永远不会重新组织对象,永远不会使用并行查询)数据通常会按照它的顺序返回不应依赖该行为插入的内容。如果问题是性能,存在哪些索引?
我收回这一点,您的后续查询运行良好,运行时间不到一分钟。感谢您的帮助!附带说明一下,我已经检查过,我们的 DBMS 实际上是在 9i 版本 9.2.0.8 上。那么有没有想到引入的任何其他效率?【参考方案2】:
这是关联子查询更好的一种情况,尤其是在表上有索引的情况下。但是,应该可以将相关子查询重写为连接。
我认为以下是等价的,没有相关的子查询:
SELECT lt.WO_NUM, lt.EMP_NUM, lt.LAB_END_DATE, lt.LAB_END_TIME
FROM (select *, rownum as r
from LAB_TIM lt
) lt join
(select wo_num, max(r) as maxrownum
from (select LAB_SEQ, wo_num, rownum as r
from LAB_TIM lt
where lab.CCN = '1' AND MAS_LOC = '1'
)
) ltsum
on lt.wo_num = ltsum.wo_num and
lt.r = ltsum.maxrownum
我有点不确定 Oracle 如何在 ORDER BY 等内容中使用 rownums。
【讨论】:
以上是关于在 Oracle 中避免相关子查询的主要内容,如果未能解决你的问题,请参考以下文章