2008-09-18 3 views
14

Ich habe eine einfache Tabelle (id INT, revision INT, comment VARCHAR(140)) mit Inhalt wie folgt kommentiert:Best-Performance-Abfrage für "Max in Gruppe auswählen"?

1|1|hallo1| 
1|2|hallo2| 
1|3|hallo3| 
2|1|hallo1| 
2|2|hallo2| 

Ich bin für eine SQL-Anweisung suchen, die Sie jeden Kommentar zurück mit der höchsten Revision:

1|3|hallo3| 
2|2|hallo2| 

Ich habe kommen mit dieser Lösung:

select id, revision, comment 
    from comments 
    where revision = (
     select max(revision) 
     from comments as f 
     where f.id = comments.id 
); 

aber es ist sehr langsam auf große Datenmengen. Gibt es bessere Abfragen, um dies zu erreichen?

+0

könnten Sie das Thema zu reflektieren Optimierung oder Performance erwägen Umbenennung? – hometoast

+0

Die Verwendung von Fensterfunktionen ist normalerweise schneller. –

Antwort

6
  1. Stellen Sie sicher, dass Ihre Indizes ordnungsgemäß eingerichtet sind. Indizierung auf ID, Revision wäre gut.

  2. Hier ist eine andere Sicht auf Ihre Abfrage. Haben Sie nicht ihren Ausführungsplan überprüft, aber wenn Sie den Index gut einrichten soll helfen:

    SELECT c.* 
        FROM comments c 
        INNER JOIN (
         SELECT id,max(revision) AS maxrev 
          FROM comments 
          GROUP BY id 
    ) b 
        ON c.id=b.id AND c.revision=b.maxrev 
    

Editted hinzuzufügen:

  1. Wenn Sie‘ Re auf SQL Server, können Sie indizierte Views als auch zu prüfen:
    http://www.microsoft.com/technet/prodtechnol/sql/2005/impprfiv.mspx

Editted wieder Informationen hinzuzufügen:

Subquery: 
25157 records 
2 seconds 
Execution plan includes an Index Seek (82%) base and a Segment (17%) 

Left Outer Join: 
25160 records 
3 seconds 
Execution plan includes two Index Scans @ 22% each with a Right Outer Merge at 45% and a Filter at 11% 

Ich würde immer noch mit der Unterabfrage gehen.

+0

Wenn Ihre Ausführungspläne versuchen, die akzeptierte Antwort (mit linkem äußerem Join) mit der Unterabfrage/Gruppenabfrage in dieser Antwort zu vergleichen, vergleichen Sie in nicht indizierten Spalten. Mit korrekten Indizes wird der linke äußere Join fast immer performanter (besonders wenn Sie viele Datensätze haben). Diese Antwort ist für eine sehr begrenzte Anzahl von Datensätzen vollkommen akzeptabel, aber wenn Sie zu 10K + Datensätzen gelangen, werden Sie bessere Ergebnisse mit der äußeren Verknüpfung finden. –

11

Hier ist eine Art und Weise, dass mit geeigneter Indizierung nicht heinously langsam sein und es verwendet keine subselect:

SELECT comments.ID, comments.revision, comments.comment FROM comments 
LEFT OUTER JOIN comments AS maxcomments 
ON maxcomments.ID= comments.ID 
AND maxcomments.revision > comments.revision 
WHERE maxcomments.revision IS NULL 

von Anfragen Angepasst hier: http://www.xaprb.com/blog/2007/03/14/how-to-find-the-max-row-per-group-in-sql-without-subqueries/

(Von Google-Suche: max Gruppe von sql)

4

Getestet mit einer unserer Tabellen, die fast 1 Million Zeilen insgesamt hat. Indizes existieren für beide Felder FIELD2 und FIELD3. Query gab 83953 Zeilen in weniger als 3 Sekunden auf unserer Dev-Box zurück.

CurrentRevision bit not null 

Dann, wenn Sie eine Änderung vornehmen, stellen Sie die Flagge auf der neuen Revision und entfernen Sie sie auf allen bisherigen:

select 
FIELD1, FIELD2, FIELD3 
from 
OURTABLE (nolock) T1 
WHERE FIELD3 = 
(
SELECT MAX(FIELD3) FROM 
OURTABLE T2 (nolock) 
WHERE T1.FIELD2=T2.FIELD2 
) 
ORDER BY FIELD2 DESC 
0

von linken Feld Idee, aber was ein zusätzliches Feld in der Tabelle über das Hinzufügen Einsen.

Ihre Anfrage würde dann einfach geworden:

select Id, 
     Comment 
from Comments 
where CurrentRevision = 1 

Dies wäre viel einfacher, auf der Datenbank und damit viel schneller.

0

Eine ziemlich saubere Möglichkeit, "neueste x durch ID" -Abfragen zu machen, ist dies.Es sollte auch ziemlich einfach sein, richtig zu indizieren.

SELECT id, revision, comment 
FROM comments 
WHERE (id, revision) IN (
    SELECT id, MAX(revision) 
    FROM comments 
    -- WHERE clause comes here if needed 
    GROUP BY id 
) 
0

Für große Tabellen finde ich, dass diese Lösung kann eine bessere Leistung hat:

SELECT c1.id, 
      c1.revision, 
      c1.comment 
     FROM comments c1 
INNER JOIN (SELECT id, 
       max(revision) AS max_revision 
       FROM comments 
      GROUP BY id) c2 
     ON c1.id = c2.id 
     AND c1.revision = c2.max_revision 
1

Analytics meine Empfehlung wäre.

select id, max_revision, comment 
from (select c.id, c.comment, c.revision, max(c.revision)over(partition by c.id) as max_revision 
     from comments c) 
where revision = max_revision; 
0

Ohne Subselects (oder temporäre Tabellen):

SELECT c1.ID, c1.revision, c1.comment 
FROM comments AS c1 
LEFT JOIN comments AS c2 
    ON c1.ID = c2.ID 
    AND c1.revision < c2.revision 
WHERE c2.revision IS NULL 
Verwandte Themen