简体   繁体   中英

Subquery in SELECT: trying to move to main query

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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM