FROM 列表中的 Oracle 相关子查询

Posted

技术标签:

【中文标题】FROM 列表中的 Oracle 相关子查询【英文标题】:Oracle correlated subquery in FROM list 【发布时间】:2009-03-04 17:44:43 【问题描述】:

我刚刚尝试在 Oracle 中的 SELECT 语句的 FROM 子句中执行关联子查询,但我收到一个错误,表明我无法执行关联(大意为 Obs.pID未被识别)。

这应该有效吗?

FROM ml.Person Person 
    JOIN ml.Obs ON Person.pID = Obs.pId
        JOIN (SELECT ObsMax2.pId, ObsMax2.hdId
                , MAX(ObsMax2.obsDate) as maxDate
                FROM ml.Obs ObsMax2
                WHERE ObsMax2.pId = Obs.pId
                    AND ObsMax2.obsDate < ?EndDate
                GROUP BY ObsMax2.pId, ObsMax2.hdId) ObsMax 
            ON Obs.pId = ObsMax.pId
                AND Obs.hdId = ObsMax.hdId
                AND Obs.obsDate = ObsMax.maxDate

我的解决方法似乎是使它成为一个不相关的子查询,并向子查询添加条件,以防止它完全运行 amuck、amuck、amu--oof 抱歉。

不过,如果可能的话,我宁愿弄清楚如何正确关联它 - 像该子查询一样工作的视图需要永远构建。

【问题讨论】:

您可能想要重构查询,而不仅仅是修复您遇到的这个语法问题。考虑在问题中添加关于您要完成的任务的说明。 @David - "Pedant" :-) @Alan - 我只是不确定我应该在多大程度上谈论这个系统的内部结构。通常,我会使用按 pID/hdID 进行分组的内置视图来获取最新的观察结果 - 但如果它是在 2009 年并且您正在查询 2008 年,则会失败。 【参考方案1】:

您可以通过使用分析函数来确定每个 pid 和 hdid 的最大 obsDate 来实现这部分查询的意图。

应该是这样的:

select ...
from   (
       SELECT pId,
              hdId,
              obsDate
              MAX(obsDate) over (partition by pId, hdId) maxDate
       FROM   ml.Obs
       WHERE  obsDate < ?EndDate
       )
where  obsDate = maxDate
/

【讨论】:

这个版本的解释方案比我想出来的要好一点,感觉也更快。我以前没有遇到过这些——谢谢! 您刚刚向我展示了如何使用 PARTITION BY,并解决了我的问题。【参考方案2】:

FROM 子句中的子查询不能引用同一 FROM 子句中的其他表。删除 ObsMax2.pId = Obs.pId 子句应该可以解决问题,并且据我所知,由于相同的子句处于连接条件中,因此会给出完全相同的结果。但是,正如您所提到的,您可能会在子查询中使用 GROUP BY 时遇到性能问题。

据我所知,您正试图从 ml.Obs 中获取单个 pID/hdId 记录,其最大 obsDate 小于 EndDate。在这种情况下,如何将子查询移到可以关联它的 WHERE 子句中呢?例如:

select ...
from
  ml.Person Person
  join ml.Obs on Person.PID = Obs.pId
where Obs.obsDate = (
    select max(obsDate)
    from ml.Obs Obs2
    where Obs2.pId = Obs.pId
      and obs2.hdId = Obs.hdId
      and Obs2.obsDate < EndDate)

【讨论】:

如果是这种情况,特别是如果该字段仅为日期类型,请注意可能返回多个人(因为重复日期)。 是的,如果 (pId, hdId, obsDate) 组合不是唯一的,那么每个 (pId, hdId) 对可以获得多条记录。我相信原始查询也会有同样的问题。 这是对 EMR(医疗记录)系统的查询。 pId 是 PersonId,hdId 标识医学观察结果(BP、Pulse、LDL 等)。因此,您不必(过多)担心重复 pid/hdid/date - 每个文档都有一个值。 但我认为这行不通 - 您可能需要两天不同的观察结果(一次访问时的 BMI 百分位数,另一次访问哮喘监测)。除非我遗漏了一些关于相关性的东西,这总是有可能的...... 关联在所有三列上完成。基本查询将从 ml.Obs 中检索一个人的所有行。然后,子查询选择 obsdate 是给定 pId、hdId 的 max() 的行。请参阅 David Aldridge 的答案以了解对此的变化(性能可能会有所不同)。【参考方案3】:

您已经为许多表格添加了前缀“ml”。但并非无处不在(例如,第一次加入)。假设您需要(对于用户/权限/其他):

在 Person.pID 上加入 ml.Obs = **ml.**Obs.pId

加入 ml.Obs Obs ON Person.pID = Obs.pId

还有其他地方也需要这样做。

如果不是这种情况,请将它们从您的查询中删除,因为它们无关紧要且会分散注意力。

【讨论】:

以上是关于FROM 列表中的 Oracle 相关子查询的主要内容,如果未能解决你的问题,请参考以下文章

FROM中的子查询不在Oracle SQL中工作

FROM 中的子查询在 Oracle SQL 中不起作用

MySQL的子查询中FROM和EXISTS子句的使用教程

hive UNION和子查询

Oracle SQL,在 SELECT 标头中的子查询中返回唯一(最大)行(在 FROM、WHERE 之前)

oracle 分组查询 子查询 统计查询 FROM加子查询临时表 查询高于平均工资 示例代码