2008-09-09 20 views
3

Betrachten Sie die Oracle emp Tabelle. Ich möchte die Angestellten mit dem besten Gehalt mit department = 20 und job = clerk bekommen. Nehmen wir außerdem an, dass es keine "empno" -Spalte gibt und dass der Primärschlüssel eine Anzahl von Spalten enthält. Sie können dies mit:SQL: Aggregatfunktion und Gruppe von

select * from scott.emp 
where deptno = 20 and job = 'CLERK' 
and sal = (select max(sal) from scott.emp 
      where deptno = 20 and job = 'CLERK') 

Dies funktioniert, aber ich habe den Test deptno = 20 und Job duplizieren = ‚CLERK‘, die ich vermeiden möchte. Gibt es eine elegantere Art, dies zu schreiben, vielleicht mit einem group by? Übrigens, wenn es darauf ankommt, verwende ich Oracle.

Antwort

3

Das folgende ist etwas überentwickelt, aber ist ein gutes SQL-Muster für "Top-X" -Abfragen.

Beachten Sie auch, dass dies in Oracle und Postgress funktioniert (ich denke), aber nicht MS SQL. Für etwas Ähnliches in MS SQL siehe Frage SQL Query to get latest price

0

Das ist großartig! Ich wusste nicht, dass man einen Vergleich von (x, y, z) mit dem Ergebnis einer SELECT-Anweisung machen könnte. Das funktioniert gut mit Oracle.

Als Randbemerkung für andere Leser fehlt der obigen Abfrage ein "=" nach "(deptno, job, sal)". Vielleicht hat der Stack Overflow Formatierer es (?).

Nochmal, danke Mark.

+0

Keine Probleme, ich habe die Antwort bearbeitet :) Es sollte ein IN sein –

2

Wenn ich bestimmt die Ziel Datenbank war ich mit Mark Nold-Lösung gehen würde, aber wenn Sie jemals etwas Dialekt Agnostiker SQL * wollen, versuchen

SELECT * 
FROM scott.emp e 
WHERE e.deptno = 20 
AND e.job = 'CLERK' 
AND e.sal = (
    SELECT MAX(e2.sal) 
    FROM scott.emp e2 
    WHERE e.deptno = e2.deptno 
    AND e.job = e2.job 
) 

* Ich glaube, das überall funktionieren soll, aber ich Habe keine Umgebungen um es zu testen.

0

In Oracle können Sie auch die EXISTS-Anweisung verwenden, die in einigen Fällen schneller ist.

Zum Beispiel ... SELECT Name, Nummer FROM cust WHERE cust IN (cust_id VON big_table SELECT) und trat> SYSDATE -1 langsam wäre.

aber SELECT Name, Nummer FROM cust c WHERE EXISTS (SELECT cust_id VON big_table WHERE cust_id = c.cust_id) und trat> SYSDATE -1 würde sehr schnell mit der richtigen Indizierung sein. Sie können dies auch mit mehreren Parametern verwenden.

0

Es gibt viele Lösungen. Sie können auch Ihr ursprüngliches Abfrage-Layout beibehalten, indem Sie einfach Tabellenaliase hinzufügen und die Spaltennamen verknüpfen. In der Abfrage würden Sie nur noch DEPTNO = 20 und JOB = 'CLERK' haben.

SELECT 
    * 
FROM 
    scott.emp emptbl 
WHERE 
    emptbl.DEPTNO = 20 
    AND emptbl.JOB = 'CLERK' 
    AND emptbl.SAL = 
    (
     select 
     max(salmax.SAL) 
     from 
     scott.emp salmax 
     where 
     salmax.DEPTNO = emptbl.DEPTNO 
     AND salmax.JOB = emptbl.JOB 
    ) 

Es könnte auch, dass das Schlüsselwort „ALL“ beachtet werden, für diese Art von Anfragen verwendet werden können, mit dem Sie die „MAX“ Funktion entfernen erlauben würde.

SELECT 
    * 
FROM 
    scott.emp emptbl 
WHERE 
    emptbl.DEPTNO = 20 
    AND emptbl.JOB = 'CLERK' 
    AND emptbl.SAL >= ALL 
    (
     select 
     salmax.SAL 
     from 
     scott.emp salmax 
     where 
     salmax.DEPTNO = emptbl.DEPTNO 
     AND salmax.JOB = emptbl.JOB 
    ) 

Ich hoffe, dass hilft und macht Sinn.

1

In Oracle würde ich es mit einer analytischen Funktion tun, so dass Sie würde nur die Tabelle emp einmal abfragen:

SELECT * 
    FROM (SELECT e.*, MAX (sal) OVER() AS max_sal 
      FROM scott.emp e 
     WHERE deptno = 20 
      AND job = 'CLERK') 
WHERE sal = max_sal 

Es ist einfacher, leichter zu lesen und effizienter zu gestalten.

Wenn Sie es ändern möchten Liste für alle Abteilungen mit diesen Daten aufzulisten, dann müssen Sie die „PARTITION BY-Klausel“ in OVER verwenden:

SELECT * 
    FROM (SELECT e.*, MAX (sal) OVER (PARTITION BY deptno) AS max_sal 
      FROM scott.emp e 
     WHERE job = 'CLERK') 
WHERE sal = max_sal 
ORDER BY deptno