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