Якщо ви зібрали відповіді поки що, очистили та вдосконалили, ви отримаєте цей чудовий запит:
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
, місця для розбіжностей немає.