Якщо ви зібрали відповіді поки що, очистили та вдосконалили, ви отримаєте цей чудовий запит:
UPDATE sales
SET status = 'ACTIVE'
WHERE (saleprice, saledate) IN (
SELECT saleprice, saledate
FROM sales
GROUP BY saleprice, saledate
HAVING count(*) = 1
);
Що набагато швидше, ніж будь-який з них. Враховує ефективність прийнятої на даний момент відповіді за фактором 10 - 15 (у моїх тестах на PostgreSQL 8.4 та 9.1).
Але це ще далеко не оптимально. Використовуйте NOT EXISTS(анти) напівз'єднання для ще кращої ефективності. EXISTSє стандартним SQL, існує вже назавжди (принаймні з PostgreSQL 7.2, задовго до того, як це питання було задано) і ідеально відповідає представленим вимогам:
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 <> fiddle тут
Стара SQL Fiddle
Унікальний ключ для ідентифікації рядка
Якщо у вас немає основного або унікального ключа для таблиці ( idу прикладі), ви можете замінити стовпчик системи ctidдля цілей цього запиту (але не для інших цілей):
AND s1.ctid <> s.ctid
Кожна таблиця повинна мати первинний ключ. Додайте його, якщо у вас його ще не було. Я пропоную serialабо IDENTITYколонку в Postgres 10+.
Пов'язані:
Як це швидше?
Підзапит в EXISTSанти-напівз'єднанні може припинити оцінку, як тільки буде знайдено першу дупу (немає сенсу шукати далі). Для базової таблиці з кількома дублікатами це лише м'яко ефективніше. З великою кількістю дублів це стає способом більш ефективним.
Виключіть порожні оновлення
Для рядків, у яких вже є status = 'ACTIVE'це оновлення, нічого не зміниться, але все ж вставити нову версію рядка за повну вартість (застосовуються незначні винятки). Зазвичай ви цього не хочете. Додайте ще одну WHEREумову, як показано вище, щоб уникнути цього та зробити це ще швидше:
Якщо statusце визначено NOT NULL, ви можете спростити:
AND status <> 'ACTIVE';
Тип даних стовпця повинен підтримувати <>оператора. Деякі типи, як jsonні. Подивитися:
Тонка різниця в обробці NULL
Цей запит (на відміну від прийнятої на даний момент відповіді Джоеля ) не вважає значення NULL рівними. Наступні два рядки для " (saleprice, saledate)кваліфікуються" як "чіткі" (хоча виглядають ідентично людському оці):
(123, NULL)
(123, NULL)
Також передається в унікальному індексі і майже в будь-якому іншому місці, оскільки значення NULL не порівнюють рівних за стандартом SQL. Подивитися:
Ото, GROUP BY, DISTINCTабо DISTINCT ON ()значення NULL , як лікувати рівні. Використовуйте відповідний стиль запиту залежно від того, чого ви хочете досягти. Ви все ще можете скористатися цим швидшим запитом, IS NOT DISTINCT FROMзамість =будь-якого чи всіх порівнянь, щоб NULL порівняти рівним. Більше:
Якщо всі стовпці, що порівнюються, визначені NOT NULL, місця для розбіжностей немає.