Как мне (или я могу) ВЫБРАТЬ DISTINCT по нескольким столбцам?
Мне нужно извлечь все строки из таблицы, где 2 столбца вместе все разные. Поэтому я хочу, чтобы все продажи, в которых не было других продаж, произошли в тот же день по той же цене. Продажи, которые являются уникальными в зависимости от дня и цены, будут обновлены до активного статуса.
Вот я и думаю:
UPDATE sales
SET status = 'ACTIVE'
WHERE id IN (SELECT DISTINCT (saleprice, saledate), id, count(id)
FROM sales
HAVING count = 1)
Но мой мозг болит дальше.
5 ответов
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
)
Если вы соберете ответы, очистите и улучшите их, вы получите этот превосходный запрос:
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
Уникальный ключ для идентификации строки
Если у вас нет первичного или уникального ключа для таблицы (id
в примере), вы можете заменить системным столбцом ctid
для целей этого запроса (но не для некоторых других целей):
AND s1.ctid <> s.ctid
Каждая таблица должна иметь первичный ключ. Добавьте еще один, если у вас его еще не было. Я предлагаю serial
или IDENTITY
колонка в Postgres 10+.
Связанные с:
Как это быстрее?
Подзапрос в EXISTS
anti-semi-join может прекратить оценку, как только будет найден первый дуплекс (нет смысла смотреть дальше). Для базовой таблицы с небольшим количеством дубликатов это лишь немного более эффективно. С большим количеством дубликатов это становится намного более эффективным.
Исключить пустые обновления
Если некоторые или несколько строк уже status = 'ACTIVE'
Ваше обновление ничего не изменит, но все равно добавит новую версию строки за полную стоимость (применяются незначительные исключения). Обычно вы этого не хотите. Добавить еще WHERE
условие, как показано выше, чтобы сделать это еще быстрее:
Если status
определено NOT NULL
Вы можете упростить до:
AND status <> 'ACTIVE';
Тонкая разница в обработке NULL
Этот запрос (в отличие от принятого в настоящее время ответа Джоэла) не рассматривает значения NULL как равные. Эти два ряда для (saleprice, saledate)
будет квалифицироваться как "отличный" (хотя выглядит идентично человеческому глазу):
(123, NULL)
(123, NULL)
Также передает уникальный индекс и почти где-либо еще, поскольку значения NULL не сравниваются равными в соответствии со стандартом SQL. Увидеть:
Ото, GROUP BY
или же DISTINCT
или же DISTINCT ON ()
трактуйте значения NULL как равные. Используйте соответствующий стиль запроса в зависимости от того, чего вы хотите достичь. Вы все еще можете использовать этот более быстрый стиль запроса, используя IS NOT DISTINCT FROM
вместо =
для любого или всех сравнений, чтобы сделать сравнение NULL равным. Больше:
Если все сравниваемые столбцы определены NOT NULL
Нет места для разногласий.
Проблема с вашим запросом состоит в том, что при использовании предложения 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, важно использовать только функцию, которая возвращает значение столбца, если есть только одна подходящая строка.
Если ваша СУБД не поддерживает различные с несколькими столбцами, как это:
select distinct(col1, col2) from table
Multi select в целом можно выполнить безопасно следующим образом:
select distinct * from (select col1, col2 from table ) as x
Поскольку это может работать на большинстве СУБД, и ожидается, что это будет быстрее, чем группировка по решению, поскольку вы избегаете функциональности группировки.
Я хочу выбрать отдельные значения из одного столбца "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)