Если вы соберете ответы, очистите и улучшите их, вы получите этот превосходный запрос:
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 <> скрипеть здесь
Old 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, нет места для разногласий.