SELECT 中的子查询:尝试移至主查询

Posted

技术标签:

【中文标题】SELECT 中的子查询:尝试移至主查询【英文标题】:Subquery in SELECT: trying to move to main query 【发布时间】:2019-07-18 09:32:42 【问题描述】:

我有一个结构如下的查询:

SELECT
    PEOPLE.ID,
    (
    SELECT MIN(R.VALUE) KEEP (DENSE_RANK FIRST ORDER BY R.DATE_OF_CREATION)
    FROM REFERENCE_NUMBERS R
    WHERE
        R.DELETED IS NULL
        AND R.PERSON_ID(+) = PEOPLE.ID
    ) AS PRIMARY_REFERENCE_NUMBER,
    PEOPLE.NAME,
    PEOPLE.DATE_OF_BIRTH
FROM
    PEOPLE
WHERE
    PEOPLE.DELETED IS NULL
    AND PEOPLE.DATE_OF_BIRTH > TO_DATE('2000-01-01','yyyy-mm-dd')

重点是一个人可以有多个参考号:但是我只想为每个人返回一个参考号,而不是多行。因此,我选择具有最小值的最早创建的参考。我使用主 SELECT 子句中的子查询来执行此操作。上面的代码有效。

我想看看是否可以将其移至主查询的一部分。从逻辑上讲,我认为应该是这样的:

SELECT
    PEOPLE.ID,
    MIN(R.VALUE) KEEP (DENSE_RANK FIRST ORDER BY R.DATE_OF_CREATION),
    PEOPLE.NAME,
    PEOPLE.DATE_OF_BIRTH
FROM
    PEOPLE,
    REFERENCE_NUMBERS R
WHERE
    PEOPLE.DELETED IS NULL
    AND PEOPLE.DATE_OF_BIRTH > TO_DATE('2000-01-01','yyyy-mm-dd')
    AND R.DELETED IS NULL
    AND R.PERSON_ID(+) = PEOPLE.ID

但是我得到了错误:ORA-00937: not a single-group function

根据https://www.techonthenet.com/oracle/errors/ora00937.php - 我需要在主查询中使用 GROUP BY 语句。但是,如果我添加该行:

GROUP BY R.VALUE

...然后我收到错误ORA-00979: not a GROUP BY expression

我觉得我快到了,但需要一些额外的指导。有人可以帮忙吗?

注意:我完全了解“新”风格的 JOIN 语法,以及人们建议使用这种语法的倾向。但是我使用“旧式”连接是有原因的。以上只是一个最低限度的可验证示例。我的真实代码要复杂得多,涉及以复杂方式连接的多个表——当涉及到这种复杂程度时,旧语法更容易理解。请尽可能以旧式语法提供答案。

【问题讨论】:

【参考方案1】:

没有什么反对在您的SELECTclause 中使用该子查询。如果您想将其移至 FROM 子句,请按 person_id 分组,以便每人获得一行,并为没有参考号的人使用外连接。

SELECT
  p.id,
  r.primary_reference_number,
  p.name,
  p.date_of_birth
FROM people p
LEFT JOIN
(
  SELECT person_id, MIN(value) KEEP (DENSE_RANK FIRST ORDER BY date_of_creation)
    AS primary_reference_number
  FROM reference_numbers
  WHERE deleted IS NULL
  GROUP BY person_id
) r ON r.person_id = p.id
WHERE p.deleted is null
AND p.date_of_birth > date '2000-01-01;

可以只是加入每个人的所有参考,然后才聚合整个混乱。这会使查询看起来更简单,但我不喜欢这种方法。您希望每个人都有一个参考号,因此请为每个人添加一个参考号。

无论如何,这将是编写此类查询的方式:

SELECT
  p.id,
  MIN(r.value) KEEP (DENSE_RANK FIRST ORDER BY r.date_of_creation)
    AS primary_reference_number,
  p.name,
  p.date_of_birth
FROM people p
LEFT JOIN reference_numbers r ON r.person_id = p.id AND r.deleted IS NULL
WHERE p.deleted is null
AND p.date_of_birth > date '2000-01-01
GROUP BY p.id, p.name, p.date_of_birth;

Oracle 强制您命名您在GROUP BY 子句中选择的所有非聚合列。根据 SQL 标准,您只需要 people.id,因为其他列在功能上依赖于它,但不幸的是,Oracle 没有此功能。

【讨论】:

谢谢,但请注意我的笔记。我想要旧式连接语法。我无法将新式语法考虑到我的实际代码中。另外,如果我有一大堆桌子怎么办?我是否需要在“分组依据”中列出每个表中的每一列 对不起,我真的没看到。我已经很多年没有使用旧的 Oracle 外连接语法了,因为事实证明它很难阅读并且一次又一次地容易出错。不过,从 ANSI 连接到该语法应该非常容易。用逗号替换LEFT JOIN,将ON移动到WHERE,比较时在外联表(或派生表)的列中添加(+)【参考方案2】:

您的查询中的第二列与其他列不同步,同样按其他列分组可以帮助如下

      SELECT
      PEOPLE.ID,
      MIN(R.VALUE) KEEP (DENSE_RANK 
       FIRST ORDER BY 
       R.DATE_OF_CREATION),
         PEOPLE.NAME,
       PEOPLE.DATE_OF_BIRTH
     FROM
     PEOPLE P,
    REFERENCE_NUMBERS R
  WHERE
  PEOPLE.DELETED IS NULL
   AND PEOPLE.DATE_OF_BIRTH > 
   TO_DATE('2000-01-01','yyyy-mm-dd')
   AND R.DELETED IS NULL
  AND R.PERSON_ID(+) = PEOPLE.ID
    GROUP BY 
    P.ID, P.NAME, P.DATE_OF_BIRTH

【讨论】:

谢谢。但正如上面的 cmets 所述 - 这是否意味着我需要在 Group By 子句中包含 每个表中的每一列?因为我的实际查询包含多个表和数百万列... 这取决于您要执行聚合的列。假设我有 2 个 id id 1 和 2 但我有这些列的数据为 1,0 : 1,1 所以如果我想得到 1 的值的计数它将是 2 所以生病分组 1 得到计数为 2在你的情况下也是如此。根据 group by 的规则,除了聚合列之外,select 子句中的每一列都应该出现在 group by 子句中

以上是关于SELECT 中的子查询:尝试移至主查询的主要内容,如果未能解决你的问题,请参考以下文章

SELECT 中的子查询或 JOIN 中的子查询?

SQL中的子查询

SELECT 子句中的子查询

MYSQLupdate/delete/select语句中的子查询

MySQL基础语法之子链接查询和特殊查询(union 和 limit)

子查询中的子查询在配置单元中不起作用