MySQL выбрать один столбец DISTINCT, с соответствующими другими столбцами


193
ID   FirstName   LastName
1      John        Doe
2      Bugs        Bunny
3      John        Johnson

Я хочу выбрать DISTINCTрезультаты из FirstNameстолбца, но мне нужны соответствующие IDи LastName.

Результирующий набор должен показывать только один John, но с ID1 и с LastNameDoe.


1
Вы хотите, чтобы фамилия принадлежала младшему идентификатору с отличным именем?
Томас Лэнгстон

3
Какова логика, которая должна идти в выборе лучших? Я думаю, вы бы хотели, чтобы и Джон Доу, и Джон Джонсон появились, потому что они два разных Джона, но это только я.
Judda

4
DISTINCTэто не функция. Все ответы с DISTINCT()ошибками. Ошибка появится, когда вы не разместите ее после SELECT.
Переполнение вопроса

1
ALL ответы, использующие скобки после слова «отличное», действительно неверны. Distinct НЕ является функцией, поэтому он не может принимать параметр. Скобки, следующие за различными, просто игнорируются. Если вы не используете PostgreSQL, где круглые скобки образуют «сложный тип данных»
Used_By_Already

Ответы:


192

попробуйте этот запрос

 SELECT ID, FirstName, LastName FROM table GROUP BY(FirstName)

16
Как мы узнаем, какая строка будет возвращена?
Уильям Энтрикен

27
@Полно прилично, вы не можете, согласно документации MySQL : «Сервер свободен выбирать любое значение из каждой группы, поэтому, если они не совпадают, выбранные значения являются неопределенными». На практике я успешно использовал этот тип запросов с предложением ORDER BY, например, вы можете добавить ORDER BY id ASC / DESC, и MySQL будет возвращать согласованные результаты каждый раз, когда вы выполняете запрос. Но я был бы уверен, должен ли кто-нибудь использовать недокументированные функции в производственной среде.
Арунас Джунвициус

2
ОП не упоминает версию MySQL.
DiEcho

2
@sinaza смотрите мой обновленный ответ для MySQL 5.7.5+для измененной GROUP BYобработки
fyrye

3
Это не работает в режиме only_full_group_by, потому что ни ID, ни LastName не являются ни агрегированными, ни частью функции группировки. Помогите!
Ihodonald

64

DISTINCTКлючевое слово на самом деле не работает так , как вы ожидаете его. Когда вы используете, SELECT DISTINCT col1, col2, col3вы фактически выбираете все уникальные кортежи {col1, col2, col3}.


14
Спасибо за указание на это Брайан. Можете ли вы привести пример того, как я мог бы использовать GROUP BY для получения тех же результатов?
н

59

Чтобы избежать потенциально неожиданных результатов при использовании GROUP BYбез агрегатной функции, как это используется в принятом ответе , потому что MySQL может свободно получить ЛЮБОЕ значение в наборе данных, сгруппированных, когда не используется агрегатная функция [sic] и возникают проблемы ONLY_FULL_GROUP_BY. Пожалуйста, рассмотрите возможность использования исключающего соединения.

Присоединение к исключению - однозначные объекты

Предполагая, что имя и фамилия индексируются однозначно (однозначно) , альтернативой GROUP BYявляется сортировка с использованием LEFT JOINдля фильтрации набора результатов, иначе называемого исключением JOIN.

Посмотреть демонстрацию

Восходящий порядок (AZ)

Чтобы получить отличное имя, упорядоченное по фамилии из AZ

запрос

SELECT t1.*
FROM table_name AS t1
LEFT JOIN table_name AS t2
ON t1.firstname = t2.firstname
AND t1.lastname > t2.lastname
WHERE t2.id IS NULL;

результат

| id | firstname | lastname |
|----|-----------|----------|
|  2 |      Bugs |    Bunny |
|  1 |      John |      Doe |

По убыванию (ZA)

Чтобы получить отличное имя, упорядоченное по фамилии из ZA

запрос

SELECT t1.*
FROM table_name AS t1
LEFT JOIN table_name AS t2
ON t1.firstname = t2.firstname
AND t1.lastname < t2.lastname
WHERE t2.id IS NULL;

результат

| id | firstname | lastname |
|----|-----------|----------|
|  2 |      Bugs |    Bunny |
|  3 |      John |  Johnson |

Затем вы можете заказать полученные данные по желанию.


Присоединение к исключению - неоднозначные сущности

Если комбинация имени и фамилии не является уникальной (неоднозначной) и у вас есть несколько строк с одинаковыми значениями, вы можете отфильтровать результирующий набор, включив условие ИЛИ в критерии JOIN, чтобы также выполнить фильтрацию по идентификатору.

Посмотреть демонстрацию

данные таблицы

(1, 'John', 'Doe'),
(2, 'Bugs', 'Bunny'),
(3, 'John', 'Johnson'),
(4, 'John', 'Doe'),
(5, 'John', 'Johnson')

запрос

SELECT t1.*
FROM table_name AS t1
LEFT JOIN table_name AS t2
ON t1.firstname = t2.firstname
AND (t1.lastname > t2.lastname
OR (t1.firstname = t1.firstname AND t1.lastname = t2.lastname AND t1.id > t2.id))
WHERE t2.id IS NULL;

результат

| id | firstname | lastname |
|----|-----------|----------|
|  1 |      John |      Doe |
|  2 |      Bugs |    Bunny |

Заказанный подзапрос

РЕДАКТИРОВАТЬ

Мой оригинальный ответ, использующий упорядоченный подзапрос , был написан до MySQL 5.7.5 , которая больше не применима, из-за изменений с ONLY_FULL_GROUP_BY. Пожалуйста, используйте приведенные выше примеры исключений.

Также важно отметить; когда ONLY_FULL_GROUP_BYотключено (исходное поведение до MySQL 5.7.5) , использование GROUP BYбез агрегатной функции может привести к неожиданным результатам, потому что MySQL может свободно выбирать ЛЮБОЕ значение в пределах группируемого набора данных [sic] .

Значение IDили lastnameзначение может быть извлечено , которое не связано с извлеченной firstnameстрокой.


ПРЕДУПРЕЖДЕНИЕ

С MySQL GROUP BYможет не дать ожидаемых результатов при использовании сORDER BY

См. Пример тестового примера

Лучший способ реализации, чтобы гарантировать ожидаемые результаты, состоит в том, чтобы фильтровать область набора результатов, используя упорядоченный подзапрос.

данные таблицы

(1, 'John', 'Doe'),
(2, 'Bugs', 'Bunny'),
(3, 'John', 'Johnson')

запрос

SELECT * FROM (
    SELECT * FROM table_name ORDER BY ID DESC
) AS t1
GROUP BY FirstName

результат

| ID | first |    last |
|----|-------|---------|
|  2 |  Bugs |   Bunny |
|  3 |  John | Johnson |

сравнение

Для демонстрации неожиданных результатов при использовании GROUP BYв сочетании сORDER BY

запрос

SELECT * FROM table_name GROUP BY FirstName ORDER BY ID DESC

результат

| ID | first |  last |
|----|-------|-------|
|  2 |  Bugs | Bunny |
|  1 |  John |   Doe |

3
Самый полный ответ на сегодняшний день. Изменение «ID desc» на «ID asc» в первом запросе позволяет нам получить «John Doe» или «John Johnson». Изменение 'ID desc' во втором запросе не имеет этого эффекта.
Карла

На postgres вам нужен ID в группе, не уверенный в MySQL.
Сачин Прасад

Будет ли GROUP BY column-A ORDER BY column-B в одном операторе SELECT всегда работать правильно с последней версией MyriaDB?
Нил Дэвис

@NealDavis В соответствии с руководством MariaDBOrdering is done after grouping. , так что Нет, не в этом сценарии использования, кроме того, MariaDB игнорирует ORDER BY в подзапросах (согласно стандарту SQL) без a LIMIT. Вы хотели бы использовать Window FunctionДля получения дополнительной информации вы должны задать свой вопрос в стеке обмена DBA , так как этот вопрос относится к MySQL
fyrye

1
@NateS Нет, GROUP BYможно выбрать любое значение в сгруппированном наборе данных, если только в этих столбцах не используется агрегатная функция для принудительного задания определенного значения. Так lastnameили idможет прийти из любого из упорядоченных рядов. Исходный пример подзапроса был приемлем по умолчанию в, MySQL <= 5.7.4но технически все еще страдает от этой проблемы. Хотя ORDER BYэто помогает предотвратить случайный выбор, это все же теоретически возможно, но со значительно меньшей вероятностью, чем без использования ORDER BYподзапроса.
фюре



3

Как насчет

`SELECT 
    my_distinct_column,
    max(col1),
    max(col2),
    max(col3)
    ...
 FROM
    my_table 
 GROUP BY 
    my_distinct_column`

2

Не уверен, что вы можете сделать это с MySQL, но вы можете использовать CTE в T-SQL

; WITH tmpPeople AS (
 SELECT 
   DISTINCT(FirstName),
   MIN(Id)      
 FROM People
)
SELECT
 tP.Id,
 tP.FirstName,
 P.LastName
FROM tmpPeople tP
JOIN People P ON tP.Id = P.Id

В противном случае вам, возможно, придется использовать временную таблицу.


1

Как указывает fyrye , принятый ответ относится к более старым версиям MySQL, в которых ONLY_FULL_GROUP_BYеще не было представлено. С MySQL 8.0.17 (используется в этом примере), если вы не отключите, ONLY_FULL_GROUP_BYвы получите следующее сообщение об ошибке:

mysql> SELECT id, firstName, lastName FROM table_name GROUP BY firstName;

ОШИБКА 1055 (42000): Выражение № 1 списка SELECT отсутствует в предложении GROUP BY и содержит неагрегированный столбец «mydatabase.table_name.id», который функционально не зависит от столбцов в предложении GROUP BY; это несовместимо с sql_mode = only_full_group_by

Один из способов обойти это, не упомянутый fyrye , но описанный в https://dev.mysql.com/doc/refman/5.7/en/group-by-handling.html , - это применить ANY_VALUE()функцию к столбцам, которые не в GROUP BYпункте ( idи lastNameв этом примере):

mysql> SELECT ANY_VALUE(id) as id, firstName, ANY_VALUE(lastName) as lastName FROM table_name GROUP BY firstName;
+----+-----------+----------+
| id | firstName | lastName |
+----+-----------+----------+
|  1 | John      | Doe      |
|  2 | Bugs      | Bunny    |
+----+-----------+----------+
2 rows in set (0.01 sec)

Как написано в вышеупомянутых документах,

В этом случае MySQL игнорирует недетерминированность значений адресов в каждой группе имен и принимает запрос. Это может быть полезно, если вам просто все равно, какое значение неагрегированного столбца выбрано для каждой группы. ANY_VALUE()не является агрегатной функцией, в отличие от таких функций, как SUM()или COUNT(). Это просто действует, чтобы подавить тест на недетерминизм.


Для разъяснения я специально избегал предлагать использовать в ANY_VALUE()качестве своего ответа, а комментарии направлены на предотвращение неоднозначных и непредсказуемых наборов результатов. Поскольку, как следует из названия функции, это может привести к получению любого значения из выбранных строк. Я бы предложил использовать MAXили MINвместо.
fyrye

0

Помните, что при использовании группировки по и порядку MySQL является ЕДИНСТВЕННОЙ базой данных, которая позволяет использовать столбцы в группе по и / или по порядку, которые не являются частью оператора выбора.

Так, например: выберите column1 из группы таблиц по column2, порядок по column3

Это не будет работать в других базах данных, таких как Postgres, Oracle, MSSQL и т. Д. В этих базах данных вам нужно будет сделать следующее

выберите столбец1, столбец2, столбец3 из группы таблиц по столбцу2, порядок по столбцу3

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


-2

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

select * from tabel_name group by FirstName

Теперь вы получили такой вывод:

ID    FirstName     LastName
2     Bugs          Bunny
1     John          Doe


Если вы хотите ответить как

ID    FirstName     LastName
1     John          Doe
2     Bugs          Bunny

затем используйте этот запрос,

select * from table_name group by FirstName order by ID

2
Это не всегда даст ожидаемые результаты при группировке с заказом по
fyrye

-3
SELECT DISTINCT(firstName), ID, LastName from tableName GROUP BY firstName

Будет лучшим выбором ИМО


32
это не сработает, оно также будет использовать идентификатор и фамилию в отдельной оценке.
Ludo - вне записи

2
это то же самое, что DISTINCT (firstName, ID, LastName)
Том Тейлор,

-4
SELECT DISTINCT (column1), column2
FROM table1
GROUP BY column1

1
DISTINCT()это не функция. Также DISTINCT и GROUP BY делают одно и то же, поэтому нет причин ставить их обоих.
Marki555 15.07.15

Это неэффективное утверждение, вы должны использовать DISTINCT или Group By, а не оба.
heshanlk
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.