2008-09-18 5 views
14

Mam prosty stół komentuje (id INT, revision INT, comment VARCHAR(140)) z niektórych treści jak poniżej:Najlepsze zapytanie o "wybierz maks. W grupie"?

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

szukam instrukcji SQL, która zwróci każdy komentarz z najwyższej Weryfikacja:

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

mam wymyślić to rozwiązanie:

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

, ale jest bardzo wolny na dużych zbiorach danych. Czy są jakieś lepsze pytania, aby to osiągnąć?

+0

można rozważyć zmianę nazwy tematu do refleksji lub optymalizację wydajności? – hometoast

+0

Korzystanie z funkcji okna jest zwykle szybsze. –

Odpowiedz

6
  1. Upewnij się, że masz właściwie skonfigurowane indeksy. Indeksowanie na id, rewizja byłaby dobra.

  2. Oto inne podejście do zapytania. Nie sprawdził swój plan wykonania, ale jeśli założyć indeks dobrze to powinno pomóc:

    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 dodać:

  1. Jeśli” re na SQL Server, może chcesz sprawdzić widoki indeksowane, a także: znowu
    http://www.microsoft.com/technet/prodtechnol/sql/2005/impprfiv.mspx

editted dodać informacje:

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% 

Wciąż bym podjął zapytanie.

+0

Jeśli twoje plany wykonania próbują porównać zaakceptowaną odpowiedź (przy użyciu lewego sprzężenia zewnętrznego) z zapytaniem o podzapytanie/grupę w tej odpowiedzi, porównujesz kolumny bez indeksowania. Dzięki poprawnym indeksom, lewe sprzężenie zewnętrzne będzie bardziej wydajne prawie za każdym razem (szczególnie, gdy masz wiele rekordów). Ta odpowiedź jest całkowicie do przyjęcia dla bardzo ograniczonej ilości rekordów, ale gdy dojdziesz do rekordów 10K +, uzyskasz lepsze wyniki przy łączeniu zewnętrznym. –

11

Oto jeden ze sposobów, które z odpowiednim indeksowania nie będzie heinously powolny i nie używa podselekcji:

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 

zaadaptowany z zapytaniami tutaj: http://www.xaprb.com/blog/2007/03/14/how-to-find-the-max-row-per-group-in-sql-without-subqueries/

(z wyszukiwarki Google: max grupie przez sql)

4

Przetestowano za pomocą jednej z naszych tabel, która zawiera prawie milion wierszy. Indeksy istnieją w obu polach FIELD2 i FIELD3. Zapytanie zwróciło 83953 wiersze w mniej niż 3 sekundy w naszym polu dev.

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

Pomysł z lewej pola, ale co o dodanie dodatkowego pola do stołu:

CurrentRevision bit not null 

Następnie po dokonaniu zmiany, należy ustawić flagę na nowej rewizji i usunąć go na wszystkie poprzednie te.

Zapytanie będzie po prostu stać:

select Id, 
     Comment 
from Comments 
where CurrentRevision = 1 

To byłoby o wiele łatwiejsze w bazie danych, a więc znacznie szybciej.

0

Jednym z dość czystych sposobów na zrobienie zapytań typu "ostatnie x przez id" jest to.Powinno to również być dość łatwe do właściwego indeksowania.

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

Dla dużych tabel Uważam, że rozwiązanie to ma lepszą wydajność:

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 byłyby moje zalecenie.

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

Bez obsługę żądań (lub tabel tymczasowych):

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