Мне нужно получить все строки из таблицы, в которой 2 столбца в сочетании все разные. Поэтому я хочу, чтобы все продажи, не имеющие других продаж, произошли в тот же день, по той же цене. Продажи, уникальные в зависимости от дня и цены, будут обновлены до активного статуса.
Итак, я думаю:
UPDATE sales
SET status = 'ACTIVE'
WHERE id IN (SELECT DISTINCT (saleprice, saledate), id, count(id)
FROM sales
HAVING count = 1)
Но мой мозг болит, идя дальше этого.


SELECT DISTINCT a,b,c FROM t
грубо эквивалентен:
SELECT a,b,c FROM t GROUP BY a,b,c
Рекомендуется привыкнуть к синтаксису GROUP BY, так как он более мощный.
По вашему запросу я бы сделал это так:
UPDATE sales
SET status='ACTIVE'
WHERE id IN
(
SELECT id
FROM sales S
INNER JOIN
(
SELECT saleprice, saledate
FROM sales
GROUP BY saleprice, saledate
HAVING COUNT(*) = 1
) T
ON S.saleprice=T.saleprice AND s.saledate=T.saledate
)
Этот запрос, хотя и правильный и принят в течение года, - это крайне неэффективный, и это необязательно. Не используйте это. Я предоставил альтернативу и некоторые объяснения в другом ответе.
разве SELECT DISTINCT a, b, c FROM t именно так не то же самое, что SELECT a, b, c FROM t GROUP BY a, b, c?
@famargar для простого случая, но они имеют разные значения семантически, и они разные с точки зрения того, что вы можете сделать для шага при построении более крупного запроса. Кроме того, люди на технических форумах часто могут быть очень сильно педантичными в отношении вещей, и я считаю, что часто полезно добавлять ласковые слова в мои сообщения в этом контексте.
Проблема с вашим запросом заключается в том, что при использовании предложения GROUP BY (что вы, по сути, делаете, используя отличные), вы можете использовать только столбцы, которые вы группируете или объединяете функции. Вы не можете использовать идентификатор столбца, потому что есть потенциально разные значения. В вашем случае всегда есть только одно значение из-за предложения HAVING, но большинство СУБД недостаточно умны, чтобы распознать это.
Однако это должно работать (и не требует соединения):
UPDATE sales
SET status='ACTIVE'
WHERE id IN (
SELECT MIN(id) FROM sales
GROUP BY saleprice, saledate
HAVING COUNT(id) = 1
)
Вы также можете использовать MAX или AVG вместо MIN, важно использовать функцию, которая возвращает значение столбца, только если есть только одна соответствующая строка.
Если вы соберете ответы, очистите и улучшите, вы получите следующий превосходный запрос:
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 <> рабочий пример здесь
Старый скрипт SQL
Если у вас нет первичного или уникального ключа для таблицы (id в примере), вы можете заменить его системным столбцом ctid для целей этого запроса (но не для некоторых других целей):
AND s1.ctid <> s.ctid
Every table should have a primary key. Add one if you didn't have one, yet. I suggest a serial or an IDENTITY column in Postgres 10+.
Связанный:
Подзапрос в анти-полусоединении EXISTS может перестать оцениваться, как только будет обнаружен первый дубликат (нет смысла искать дальше). Для базовой таблицы с несколькими дубликатами это лишь немного эффективнее. При большом количестве дубликатов путь становится более эффективным.
Для строк, в которых уже есть status = 'ACTIVE', это обновление ничего не изменит, но все равно вставит новую версию строки за полную стоимость (применяются незначительные исключения). Обычно вы этого не хотите. Добавьте еще одно условие WHERE, как показано выше, чтобы избежать этого и сделать его еще быстрее:
Если status определен как NOT NULL, вы можете упростить:
AND status <> 'ACTIVE';
Тип данных столбца должен поддерживать оператор <>. Некоторые типы, такие как json, этого не делают. Видеть:
Этот запрос (в отличие от в настоящее время принятый ответ Джоэла) не обрабатывает значения NULL как равные. Следующие две строки для (saleprice, saledate) можно квалифицировать как "отдельные" (хотя они выглядят идентично человеческому глазу):
(123, NULL)
(123, NULL)
Также передается уникальный индекс и почти везде, поскольку значения NULL не сравниваются как равные в соответствии со стандартом SQL. Видеть:
OTOH, GROUP BY, DISTINCT или DISTINCT ON () обрабатывают значения NULL как равные. Используйте соответствующий стиль запроса в зависимости от того, чего вы хотите достичь. Вы по-прежнему можете использовать этот более быстрый запрос с IS NOT DISTINCT FROM вместо = для любого или всех сравнений, чтобы сделать сравнение NULL равным. Более:
Если все сравниваемые столбцы определены как NOT NULL, нет места разногласиям.
Хороший ответ. Я работаю с sql-сервером, поэтому первое предложение использовать кортеж с проверкой IN () мне не пришло бы в голову. Предложение не существует, как правило, заканчивается тем же планом выполнения на сервере sql, что и внутреннее соединение.
Отлично. Объяснение значительно увеличивает ценность ответа. У меня почти возникает соблазн провести несколько тестов с Oracle, чтобы увидеть, как планы сравниваются с Postgres и SQLServer.
count(*) менее эффективен, чем что-то вроде: count(saleprice) (если точность не критична, тогда да, используйте count(*))
@alairock: Где ты это взял? Для Postgres противоположный истинно. При подсчете всех строк count(*) эффективнее count(<expression>) на более. Просто попробуйте. Postgres предлагает более быструю реализацию этого варианта агрегатной функции. Может быть, вы путаете Postgres с какой-то другой СУБД?
@alairock: Я оказался соавтором этой страницы, и на ней ничего подобного не говорится.
Зачем здесь вообще нужен HAVING COUNT(*) = 1?
@trthhrtz: Потому что OP ищет уникальные комбинации.
@ErwinBrandstetter, вы всегда в курсе своих ответов по всей стопке. Вы помогали на протяжении многих лет почти невообразимым количеством способов. Что касается этого примера, я знал несколько разных способов решения моей проблемы, но я хотел увидеть, что кто-то проверил эффективность между вариантами. Спасибо.
Разве «оптимальная» версия действительно не возвращает неправильный результат? Поскольку предложение «имеющий» из предыдущего запроса отсутствует, последний удалит все повторяющиеся строки из набора результатов ... Или я что-то упустил?
@Wecherowski: запрос обновляет все строки, в которых нет дубликатов - по запросу. Нет пункта HAVING, для начала мы ничего не собираем.
Я хочу выбрать отдельные значения из одного столбца «GrondOfLucht», но они должны быть отсортированы в порядке, указанном в столбце «Сортировка». Я не могу получить отдельные значения только одного столбца, используя
Select distinct GrondOfLucht,sortering
from CorWijzeVanAanleg
order by sortering
Это также даст столбцу «сортировку», и поскольку «GrondOfLucht» И «сортировка» не уникальны, результатом будут ВСЕ строки.
используйте ГРУППУ для выбора записей 'GrondOfLucht' в порядке, заданном 'сортировкой
SELECT GrondOfLucht
FROM dbo.CorWijzeVanAanleg
GROUP BY GrondOfLucht, sortering
ORDER BY MIN(sortering)
Это в основном объясняет, что делает принятый ответ, но я бы рекомендовал не использовать такие имена для примера (по крайней мере, перевести их). PS: Я рекомендую всегда называть все по-английски во всех проектах, даже если вы нидерландский.
Если ваша СУБД не поддерживает отдельные столбцы с несколькими столбцами, например:
select distinct(col1, col2) from table
Как правило, множественный выбор можно безопасно выполнять следующим образом:
select distinct * from (select col1, col2 from table ) as x
Поскольку это может работать на большинстве СУБД, и ожидается, что это будет быстрее, чем группировка по решению, поскольку вы избегаете функциональности группировки.
Кто-нибудь знает, как я могу добиться того же в запросе критериев? У меня есть sql: select col1, col2, abs (sum (DISTINCT col3 * col4)) У меня есть это мой запрос, например select (cb.col1, col2, expr (sum (prod))). Independent (true). Но это не дает мне отличия от значений col3.