DISTINCT только для одного столбца


156

Допустим, у меня есть следующий запрос.

SELECT ID, Email, ProductName, ProductModel FROM Products

Как я могу изменить его, чтобы он не возвращал повторяющиеся электронные письма?

Другими словами, когда несколько строк содержат одно и то же электронное письмо, я хочу, чтобы результаты включали только одну из этих строк (предпочтительно последнюю). Дубликаты в других столбцах должны быть разрешены.

Пункты как DISTINCTи, GROUP BYкажется, работают на целые строки. Так что я не уверен, как подойти к этому.


2
Хорошо, вам нужно использовать PARTITION или использовать два оператора выбора?
CarneyCode

А что должно быть показано, если есть, скажем, 2 строки с одним и тем же Email, но с другим ProductName? (Предпочтительно последний) не ясна. Последний по какому заказу?
ypercubeᵀᴹ

@ypercube Как указано в вопросе, желательно последний. Тем не менее, это не очень важно для меня. Я просто хочу одного из них.
Джонатан Вуд

1
Вы можете посмотреть на следующие вопросы: вопрос1 , вопрос2 или вопрос3 .
Marian

Почему вы не можете использовать: ВЫБЕРИТЕ DISTINCT Электронная почта, ID, ProductName, ProductModel ИЗ продуктов?
Рик Хендерсон

Ответы:


186

Если вы используете SQL Server 2005 или выше, используйте это:

SELECT *
  FROM (
                SELECT  ID, 
                        Email, 
                        ProductName, 
                        ProductModel,
                        ROW_NUMBER() OVER(PARTITION BY Email ORDER BY ID DESC) rn
                    FROM Products
              ) a
WHERE rn = 1

РЕДАКТИРОВАТЬ: Пример с использованием предложения where:

SELECT *
  FROM (
                SELECT  ID, 
                        Email, 
                        ProductName, 
                        ProductModel,
                        ROW_NUMBER() OVER(PARTITION BY Email ORDER BY ID DESC) rn
                    FROM Products
                   WHERE ProductModel = 2
                     AND ProductName LIKE 'CYBER%'

              ) a
WHERE rn = 1

4
Я должен исследовать этот пункт PARTITION, никогда прежде не видел его в действии. Спасибо за пример
LorenVS

@ Cybernate Одно осложнение: мой внутренний SELECTнуждается в WHEREсостоянии. Я думаю, что номера строк будут назначены для всех строк в таблице. Этот синтаксис немного за мной. Есть ли шанс обновления, которое гарантировало бы одну строку с определенным адресом электронной почты, который соответствует WHEREусловию?
Джонатан Вуд

1
Вы можете добавить предложение where к внутреннему SQL. Я обновлю сообщение, как только смогу получить доступ к своему ноутбуку
Chandu

1
Обновил сообщение с примером, используя предложение where.
Чанду

1
Я работаю правильно, только если в моем запросе нет JOIN s. Как только у меня есть JOIN, ROW_NUMBERвозвращает гораздо более высокие значения, чем «1».
Уве Кейм

10

Предполагается, что SQL Server 2005+ и ваше определение «последний» является максимальным PK для данного письма

WITH CTE AS
(
SELECT ID, 
       Email, 
       ProductName, 
       ProductModel, 
       ROW_NUMBER() OVER (PARTITION BY Email ORDER BY ID DESC) AS RowNumber 
FROM   Products
)
SELECT ID, 
       Email, 
       ProductName, 
       ProductModel
FROM CTE 
WHERE RowNumber = 1

6

Когда вы используете, DISTINCTдумайте об этом как о отдельной строке, а не столбце. Он вернет только те строки, столбцы которых не совпадают в точности.

SELECT DISTINCT ID, Email, ProductName, ProductModel
FROM Products

----------------------
1 | something@something.com | ProductName1 | ProductModel1
2 | something@something.com | ProductName1 | ProductModel1

Запрос будет возвращать обе строки, потому что IDстолбец отличается. Я предполагаю, что IDстолбец является IDENTITYстолбцом, который увеличивается, если вы хотите вернуть последний, я рекомендую что-то вроде этого:

SELECT DISTINCT TOP 1 ID, Email, ProductName, ProductModel
FROM Products
ORDER BY ID DESC

Функция TOP 1вернет только первую запись, упорядочив ее по IDубыванию, она вернет результаты с последней строкой первой. Это даст вам последнюю запись.


2
Как указано в вопросе, я вижу, что DISTINCT работает на весь ряд. Я хочу сделать, как вы предлагаете выше, но каждый раз, когда электронное письмо дублируется в результатах (не один раз).
Джонатан Вуд

В этом случае я бы рекомендовал идти с ответом @Cybernate. Это должно делать именно то, что вам нужно.
jon3laze

4

Вы можете сделать это с помощью функции GROUP BY

SELECT ID, Email, ProductName, ProductModel FROM Products GROUP BY Email


16
Столбец «Products.ID» недопустим в списке выбора, поскольку он не содержится ни в статистической функции, ни в предложении GROUP BY.
Palota

2
Это не работает без использования чего-то вроде MAX (ID), MAX (ProductName), MAX (ProductModel) для других столбцов
avl_sweden

2
В postgres вам нужна только агрегатная функция для столбца, которая будет использоваться в выражении group by, например SELECT id, max(email) AS email FROM tbl GROUP by email. В SQL-сервере ВСЕ столбцы в SELECTпредложении должны быть в статистической функции. Это кусает меня каждый раз, когда я возвращаюсь.
Брюс Пирсон

Это никогда не сработает. Это плохое решение
Дэн А.С.

1

Для доступа вы можете использовать запрос SQL Select, который я представляю здесь:

Например, у вас есть эта таблица:

Cliente || NOMBRES || MAIL

888 || T800 ARNOLD || t800.arnold@cyberdyne.com

123 || ДЖОН КОННОР || s.connor@skynet.com

125 || Сара Коннор ||s.connor@skynet.com

И вам нужно выбрать только отдельные письма. Вы можете сделать это с этим:

SQL SELECT:

SELECT MAX(p.CLIENTE) AS ID_CLIENTE
, (SELECT TOP 1 x.NOMBRES 
    FROM Rep_Pre_Ene_MUESTRA AS x 
    WHERE x.MAIL=p.MAIL 
     AND x.CLIENTE=(SELECT MAX(l.CLIENTE) FROM Rep_Pre_Ene_MUESTRA AS l WHERE x.MAIL=l.MAIL)) AS NOMBRE, 
p.MAIL
FROM Rep_Pre_Ene_MUESTRA AS p
GROUP BY p.MAIL;

Вы можете использовать это, чтобы выбрать максимальный идентификатор, имя корреспондента для этого максимального идентификатора, вы можете добавить любой другой атрибут таким образом. Затем в конце вы помещаете отдельный столбец для фильтрации и группируете его только по последнему отдельному столбцу.

Это принесет вам максимальный идентификатор с соответствующими данными, вы можете использовать min или любые другие функции, и вы скопируете эту функцию для подзапросов.

Этот выбор вернет:

Cliente || NOMBRES || MAIL

888 || T800 ARNOLD || t800.arnold@cyberdyne.com

125 || Сара Коннор ||s.connor@skynet.com

Не забудьте проиндексировать выбранные столбцы, и отдельный столбец не должен содержать числовые данные в верхнем или нижнем регистре, иначе он не будет работать. Это будет работать только с одним заказным письмом. Удачного кодирования !!!


0

Причина DISTINCTи GROUP BYработа с целыми строками заключается в том, что ваш запрос возвращает целые строки.

Чтобы помочь вам понять: попробуйте вручную написать, что должен возвращать запрос, и вы увидите, что в столбцах, не дублирующихся, не однозначно.

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


@JohnFix Я хочу вернуть целые строки. Я просто не хочу, чтобы строки возвращались, когда результаты уже содержат строку с таким же значением в столбце «Электронная почта».
Джонатан Вуд

Так как же решить, какой из них вернуть? Вы действительно хотите запрос, который возвращает произвольную строку для каждого электронного письма. Это действительно пахнет, как будто вам, возможно, придется переосмыслить проблему, которую вы пытаетесь решить. Практически каждый раз, когда мне задают этот вопрос (и он часто возникает), оказывается, что разработчик не продумал последствия такого поведения в приложении.
JohnFx

6
У меня действительно проблемы с следованием твоей логике. Как указано в вопросе, я бы предпочел последний (отсортированный по ID). Да, если бы он выбрал случайную строку, это было бы хорошо. И да, я думал об этом.
Джонатан Вуд

0

Попробуй это

;With Tab AS (SELECT DISTINCT Email FROM  Products)
SELECT Email,ROW_NUMBER() OVER(ORDER BY Email ASC) AS  Id FROM Tab
ORDER BY Email ASC

-2

Попробуй это:

SELECT ID, Email, ProductName, ProductModel FROM Products WHERE ID IN (SELECT MAX(ID) FROM Products GROUP BY Email)

2
Почему мы должны попробовать это? Почему это лучше, чем другие ответы, опубликованные здесь за последние 8 лет? Если вы хотите поделиться лучшим способом решения проблемы, вам нужно объяснить, почему вы рекомендуете это.
Дхарман
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.