Jeśli połączysz dotychczasowe odpowiedzi, posprzątasz i ulepszysz, dojdziesz do tego wyższego zapytania:
UPDATE sales
SET status = 'ACTIVE'
WHERE (saleprice, saledate) IN (
SELECT saleprice, saledate
FROM sales
GROUP BY saleprice, saledate
HAVING count(*) = 1
);
Co jest znacznie szybsze niż którekolwiek z nich. Nukuje wydajność obecnie akceptowanej odpowiedzi przez współczynnik 10-15 (w moich testach na PostgreSQL 8.4 i 9.1).
Ale wciąż nie jest to optymalne. Użyj NOT EXISTS
(anty) półsprzężenia, aby uzyskać jeszcze lepszą wydajność. EXISTS
jest standardowym SQL, istnieje już od zawsze (przynajmniej od PostgreSQL 7.2, na długo przed tym pytaniem) i doskonale spełnia przedstawione wymagania:
UPDATE sales s
SET status = 'ACTIVE'
WHERE NOT EXISTS (
SELECT FROM sales s1 -- SELECT list can be empty for EXISTS
WHERE s.saleprice = s1.saleprice
AND s.saledate = s1.saledate
AND s.id <> s1.id -- except for row itself
)
AND s.status IS DISTINCT FROM 'ACTIVE'; -- avoid empty updates. see below
db <> skrzypce tutaj
Old SQL Fiddle
Unikalny klucz do identyfikacji wiersza
Jeśli nie masz klucza podstawowego lub unikalnego dla tabeli ( id
w tym przykładzie), możesz zastąpić kolumnę systemową ctid
na potrzeby tego zapytania (ale nie do innych celów):
AND s1.ctid <> s.ctid
Każda tabela powinna mieć klucz podstawowy. Dodaj jeden, jeśli jeszcze go nie masz. Sugeruję kolumnę serial
lub IDENTITY
Postgres 10+.
Związane z:
Jak to jest szybsze?
Podkwerenda w łączeniu EXISTS
częściowym może przestać oceniać, gdy tylko zostanie znaleziony pierwszy dupe (nie ma sensu szukać dalej). W przypadku tabeli podstawowej z kilkoma duplikatami jest to tylko nieco bardziej wydajne. Z dużą ilością powtórzeń staje sposób bardziej efektywny.
Wyklucz puste aktualizacje
W przypadku wierszy, które już mają status = 'ACTIVE'
tę aktualizację, nic nie zmieni, ale nadal wstaw nową wersję wiersza po pełnym koszcie (obowiązują niewielkie wyjątki). Zwykle tego nie chcesz. Dodaj kolejny WHERE
warunek, jak pokazano powyżej, aby tego uniknąć i uczynić go jeszcze szybszym:
Jeśli status
jest zdefiniowane NOT NULL
, możesz uprościć:
AND status <> 'ACTIVE';
Typ danych kolumny musi obsługiwać <>
operatora. Niektóre typy jak json
nie. Widzieć:
Subtelna różnica w obsłudze NULL
To zapytanie (w przeciwieństwie do obecnie akceptowanej odpowiedzi Joela ) nie traktuje wartości NULL jako równych. Poniższe dwa wiersze (saleprice, saledate)
kwalifikują się jako „odrębne” (choć wyglądają identycznie jak ludzkie oko):
(123, NULL)
(123, NULL)
Przechodzi również w unikalny indeks i prawie wszędzie indziej, ponieważ wartości NULL nie są równe zgodnie ze standardem SQL. Widzieć:
OTOH, GROUP BY
, DISTINCT
lub DISTINCT ON ()
wartości NULL traktować jako równe. Użyj odpowiedniego stylu zapytania w zależności od tego, co chcesz osiągnąć. Nadal możesz użyć tego szybszego zapytania IS NOT DISTINCT FROM
zamiast z =
jakimkolwiek lub wszystkimi porównaniami, aby wyrównać NULL. Więcej:
Jeśli wszystkie porównywane kolumny są zdefiniowane NOT NULL
, nie ma miejsca na spory.