Как мне (или я могу) ВЫБРАТЬ DISTINCT для нескольких столбцов?

StackOverflow https://stackoverflow.com/questions/54418

Вопрос

Мне нужно получить все строки из таблицы, в которой два объединенных столбца разные.Поэтому я хочу, чтобы все продажи, у которых нет других продаж, произошли в тот же день по той же цене.Продажи, уникальные в зависимости от дня и цены, будут обновлены до активного статуса.

Поэтому я думаю:

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

Поскольку это может работать на большинстве СУБД, и ожидается, что это будет быстрее, чем группировка по решению, поскольку вы избегаете функции группировки.

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top