Frage

Betrachten Sie das Orakel emp Tisch.Ich möchte die Mitarbeiter mit dem Top-Gehalt gewinnen department = 20 Und job = clerk.Nehmen Sie außerdem an, dass es keine Spalte „empno“ gibt und dass der Primärschlüssel mehrere Spalten umfasst.Sie können dies tun mit:

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

Das funktioniert, aber ich muss den Test deptno = 20 und job = 'CLERK' duplizieren, was ich gerne vermeiden würde.Gibt es eine elegantere Möglichkeit, dies zu schreiben, vielleicht mit a group by?Übrigens, wenn das wichtig ist, verwende ich Oracle.

War es hilfreich?

Lösung

Das Folgende ist etwas überentwickelt, stellt aber ein gutes SQL-Muster für „Top-x“-Abfragen dar.

SELECT 
 * 
FROM 
 scott.emp
WHERE 
 (deptno,job,sal) IN
 (SELECT 
   deptno,
   job,
   max(sal) 
  FROM 
   scott.emp
  WHERE 
   deptno = 20 
   and job = 'CLERK'
  GROUP BY 
   deptno,
   job
  )

Beachten Sie auch, dass dies in Oracle und Postgress funktioniert (glaube ich), aber nicht in MS SQL.Für etwas Ähnliches in MS SQL siehe Frage SQL-Abfrage, um den aktuellen Preis zu erhalten

Andere Tipps

Wenn ich mir der Zieldatenbank sicher wäre, würde ich mich für die Lösung von Mark Nold entscheiden, aber wenn Sie jemals ein dialektunabhängiges SQL* wünschen, versuchen Sie es

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 sollte überall funktionieren, aber ich habe nicht die Umgebungen, um es zu testen.

In Oracle würde ich es mit einer analytischen Funktion machen, sodass Sie die emp-Tabelle nur einmal abfragen würden:

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.

Wenn Sie es ändern möchten, um diese Informationen für alle Abteilungen aufzulisten, müssen Sie die Klausel „PARTITION BY“ 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

Das ist großartig!Ich wusste nicht, dass man einen Vergleich von (x, y, z) mit dem Ergebnis einer SELECT-Anweisung durchführen kann.Das funktioniert hervorragend mit Oracle.

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

Nochmals vielen Dank, Mark.

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

Zum Beispiel...Wählen Sie Name, Nummer von Cust Where Cust in (Wählen Sie Cust_id von big_table) und eingegeben> sysdate -1 wäre langsam.

Wählen Sie jedoch den Namen, die Nummer von Cust CWo existiert WHERE cust_id=c.cust_id ) Und eingegeben> sysdate -1 wäre sehr schnell mit ordnungsgemäßer Indexierung.Sie können dies auch mit mehreren Parametern verwenden.

Es gibt viele Lösungen.Sie könnten auch Ihr ursprüngliches Abfragelayout beibehalten, indem Sie einfach Tabellenaliase hinzufügen und die Spaltennamen verknüpfen. Sie hätten DEPTNO = 20 und JOB = 'CLERK' immer noch nur einmal in der Abfrage.

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 ist auch zu beachten, dass für diese Art von Abfragen das Schlüsselwort „ALL“ verwendet werden kann, wodurch Sie die Funktion „MAX“ entfernen können.

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, das hilft und macht Sinn.

Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top