I have a query with the following structure:
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')
The point of this is that a person can have multiple reference numbers: however I only want to return one reference number per person, and not multiple lines. So I select the earliest-created reference with the minimum value. I do this with a subquery within the main SELECT clause. The above code works.
I want to see if I can move this to be part of the primary query. Logically, I would have thought that it should be as follows:
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
However I get the error: ORA-00937: not a single-group function
According to https://www.techonthenet.com/oracle/errors/ora00937.php - I need a GROUP BY statement within the main query. However if I add the line:
GROUP BY R.VALUE
... then I get the error ORA-00979: not a GROUP BY expression
I feel I'm nearly there, but just need some extra guidance. Can someone please help?
Note: I am fully aware of the "new" style JOIN syntax, and the tendency for people to advise using this syntax. However I use the "old-style" joins for a reason. The above is simply a minimum verifiable example. My real code is vastly more complex, involving multiple tables joined in complex ways - and the old syntax is more understandable when it comes to that level of complexity. Kindly provide answers in the old-style syntax where possible.
There is nothing against having that sub query in your SELECT
clause. If you want to move it to your FROM
clause, group by person_id
in order to get one row per person and use an outer join for people without a reference number.
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;
You could just join every person with all their references instead and only then aggregate the whole mess. This would make the query look simpler, but I don't like this approach. You want one reference number per person, so join one reference number to each person.
Anyway, this would be the way to write such query:
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 forces you to name all non-aggregated columns you are selecting in the GROUP BY
clause. Per SQL standard you'd only need the people.id
as the other columns are functionally dependent on it, but Oracle doesn't feature this, unfortunately.
The 2nd column in your query is not falling in sync with the other columns likewise group by other columns could help as below
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
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.