Как мне (или я могу) ВЫБРАТЬ DISTINCT для нескольких столбцов?
-
09-06-2019 - |
Вопрос
Мне нужно получить все строки из таблицы, в которой два объединенных столбца разные.Поэтому я хочу, чтобы все продажи, у которых нет других продаж, произошли в тот же день по той же цене.Продажи, уникальные в зависимости от дня и цены, будут обновлены до активного статуса.
Поэтому я думаю:
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
)
Другие советы
Если вы соберете ответы на данный момент, очистите и улучшите, вы получите следующий превосходный запрос:
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
БД<>скрипка здесь
Старая скрипта SQL
Уникальный ключ для идентификации строки
Если у вас нет первичного или уникального ключа для таблицы (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, важно использовать функцию, которая возвращает значение столбца только в том случае, если есть только одна соответствующая строка.
Я хочу выбрать отдельные значения из одного столбца «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)
Если ваша СУБД не поддерживает различение нескольких столбцов, например:
select distinct(col1, col2) from table
Мультивыбор в целом можно безопасно выполнить следующим образом:
select distinct * from (select col1, col2 from table ) as x
Поскольку это может работать на большинстве СУБД, и ожидается, что это будет быстрее, чем группировка по решению, поскольку вы избегаете функции группировки.