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】:没有什么反对在您的SELECT
clause 中使用该子查询。如果您想将其移至 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 中的子查询:尝试移至主查询的主要内容,如果未能解决你的问题,请参考以下文章
MYSQLupdate/delete/select语句中的子查询