在 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_SEQWO_NUM 的组合在 LAB_TIM 中不是唯一的吗?您是否需要在CCNMAS_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

但我不清楚CCNMAS_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 中避免相关子查询的主要内容,如果未能解决你的问题,请参考以下文章

FROM 列表中的 Oracle 相关子查询

Oracle SQL - 多级相关子查询不起作用

Oracle with重用子查询

将 TOP 1 相关子查询从 SQL Server 翻译到 Oracle

如何避免重复冗长的相关子查询?

为啥此相关子查询在 Oracle 和 SQL Server 中的工作方式不同