2012-11-02 15 views
9

Chcę zaktualizować 10 najlepszych wartości kolumny w tabeli. Mam trzy kolumny; id, account i accountrank. Aby uzyskać 10 najlepszych wartości można użyć następujących:Zaktualizuj najlepsze wartości N za pomocą PostgreSQL

SELECT * FROM accountrecords  
ORDER BY account DESC 
LIMIT 10; 

Co chciałbym zrobić, to ustawić wartość w accountrank być seria 1 - 10, na podstawie wielkości account. Czy można to zrobić w PostgreSQL?

+2

Jeśli Twoja wersja poatgra ma wartość 8.4 lub wyższą, możesz użyć funkcji okienkowych + rank() lub row_number(). – wildplasser

Odpowiedz

22
UPDATE accountrecords a 
SET accountrank = sub.rn 
FROM (
    SELECT id, row_number() OVER (ORDER BY account DESC NULLS LAST) AS rn 
    FROM accountrecords  
    ORDER BY account DESC NULLS LAST 
    LIMIT 10 
    ) sub 
WHERE sub.id = a.id; 

Łączenie w tabeli jest zwykle szybciej niż skorelowane podzapytania. Jest również krótszy.

Gwarantowane są odrębne liczby z window function row_number(). Użyj rank() (lub prawdopodobnie dense_rank()), jeśli chcesz, aby wiersze o równych wartościach dla account udostępniały ten sam numer.

Tylko jeśli nie może być NULL wartości w account, trzeba dołączać NULLS LAST dla malejąco porządek lub NULL wartości porządek na szczycie:

Jeśli nie może być współbieżne zapisu dostęp, powyższe zapytanie podlega warunkowi wyścigu . Rozważmy:

Jednakże, jeśli tak było, cała koncepcja ciężko kodowania w pierwszej dziesiątce byłoby wątpliwe podejście do początku.

1

Oczywiście, możesz użyć instrukcji select w podzapytaniu. Generowanie porządku rangowego nie jest trywialne, ale tutaj jest przynajmniej jeden sposób na zrobienie tego. Nie testowałem tego, ale przy mojej głowie:

update accountrecords 
set accountrank = 
    (select count(*) + 1 from accountrecords r where r.account > account) 
where id in (select id from accountrecords order by account desc limit 10); 

Ma to dziwactwo, że jeśli dwa rekordy mają taką samą wartość dla account, potem dostaną taką samą rangę. Można uznać, że cecha ... :-)