SQL / mysql - выбрать отдельный / уникальный, но вернуть все столбцы?

374
SELECT DISTINCT field1, field2, field3, ......   FROM table

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

SELECT DISTINCT field1, * from table
aryaxt
источник
12
Почему у SELECT DISTINCT * FROM tableвас не работает?
ypercubeᵀᴹ
19
Если в вашей таблице есть PK, все строки должны быть distinctпо определению. Если вы пытаетесь просто выбрать, DISTINCT field1но каким-то образом вернуть все другие столбцы, что должно произойти для тех столбцов, которые имеют более одного значения для определенного field1значения? Вы должны будете использовать GROUP BYи некоторые виды агрегации для других столбцов, например.
Мартин Смит
1
Если вам нужны повторяющиеся строки, а не только отдельные строки, удалите ключевое слово.
Гиперборей
2
Не могли бы вы привести пример того, как вы ожидаете, что результаты будут выглядеть? Пока что я не могу понять смысл вашего желаемого запроса.
рекурсивный
3
Вот ответ на аналогичный вопрос, который вы задали, вам нужно сначала получить отдельный столбец с их идентификаторами, а затем соединить его с исходной таблицей. ВЫБЕРИТЕ DISTINCT в одном столбце, верните несколько других столбцов
yadavr

Ответы:

407

Вы ищете группу по:

select *
from table
group by field1

Которые иногда могут быть написаны с внятным выражением:

select distinct on field1 *
from table

Однако на большинстве платформ ни один из вышеперечисленных не будет работать, поскольку поведение других столбцов не определено. (Первый работает в MySQL, если это то, что вы используете.)

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

На некоторых платформах (например, PostgreSQL, Oracle, T-SQL) это можно сделать напрямую с помощью оконных функций:

select *
from (
   select *,
          row_number() over (partition by field1 order by field2) as row_number
   from table
   ) as rows
where row_number = 1

На других (MySQL, SQLite) вам нужно написать подзапросы, которые заставят вас объединить всю таблицу с самим собой ( пример ), поэтому не рекомендуется.

Дени де Бернарди
источник
10
Запрос не будет разбирать для меня и выдает ошибку: The ranking function "row_number" must have an ORDER BY clause. Нам нужно добавить порядок по предложению после разделения по полю1. Так что правильный запрос будет select * from ( select *, row_number() over (partition by field1 order by orderbyFieldName) as row_number from table ) as rows where row_number = 1
Ankur-m
1
Спасибо! Я был в той же проблеме, и решение было GROUP BY
Хоакин Иурчук
2
Также в Oracle (Oracle SQL Developer) вы не можете указать select *, row_number() over (partition by field1 order by field2) as row_number from table. Вы должны явно использовать имя таблицы / псевдоним в запросе выбораselect **table**.*, row_number() over (partition by field1 order by field2) as row_number from table
meta4
1
@jarlh: Может быть ... сегодня. Как вы можете заметить, этому ответу почти 7 лет, момент, когда этого не произошло, насколько я могу вспомнить из прошлого, когда я был активным. Вы можете пометить и / или изменить ответ, если считаете, что это необходимо.
Дени де Бернарди
2
select distinct on (field1) * from table; работает также в PostgreSQL
Чилиану Богдан
61

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

Подумайте об этом так: если ваше field1происходит более одного раза, какое значение field2будет указано в списке (учитывая, что у вас есть одно и то же значение field1в двух строках, но два разных значения field2в этих двух строках).

Однако вы можете использовать агрегатные функции (явно для каждого поля, которое вы хотите показать) и использовать GROUP BYвместо DISTINCT:

SELECT field1, MAX(field2), COUNT(field3), SUM(field4), .... FROM table GROUP BY field1
Кости Сьюдату
источник
4
+1 за это решение. Таким образом , мы можем сделать SELECT field1, MIN(field2), MIN(field3), MIN(field4), .... FROM table GROUP BY field1, и field2, 3, 4 ,,, не должны быть целыми числами (или другие цифры), они могут быть символьные поля, а
ножке
Работал хорошо, пока я не застрял в логической колонке. Значения столбца MIN (динамические) изменяются на false, даже если это было истиной. Любая другая агрегатная функция, доступная для обращения к логическому - signonsridhar 6 минут назад. Сумма (динамическая) изменена с ложного на 1
signonsridhar
1
Отличное предложение привело меня к моему решению, которое я считаю более универсальным - взгляните!
Гаррет Симпсон
@signonsridhar приведите ваш логический тип к int и используйте сумму; напримерsum(cast(COL as int)) > 0
Дрю
26

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

Если вы используете GROUP BY без агрегатной функции, то в любом поле, которое вы указали в поле GROUP BY, будет указано ваше поле DISTINCT.

Если вы делаете свой запрос:

SELECT * from table GROUP BY field1;

Он покажет все ваши результаты на основе одного экземпляра field1.

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

SELECT * FROM persons GROUP BY name;

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

SELECT * FROM persons GROUP BY lastName, firstName;

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

rocklandcitizen
источник
Как упоминалось в принятом ответе, будет работать для большинства воплощений SQL - только для MYSQL
Гаррет Симпсон
15
SELECT  c2.field1 ,
        field2
FROM    (SELECT DISTINCT
                field1
         FROM   dbo.TABLE AS C
        ) AS c1
        JOIN dbo.TABLE AS c2 ON c1.field1 = c2.field1
штормовой
источник
Почему есть, C aliasкогда он может работать без него? в очередиFROM dbo.TABLE AS C
Талха
2
Я считаю, что это связано с моим использованием RedGate SQLPrompt. Как я настроил, он всегда добавляет псевдонимы - даже если они не нужны. Это там «на всякий случай»
Stormy
Это выглядело многообещающе для меня, но все равно вернуло все строки, а не отдельное поле1. :(
Майкл Лихорадка
13

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

Сократить количество результатов запроса с помощью оператора GROUP BY легко, если вы не запрашиваете дополнительную информацию. Давайте предположим, что вы получили следующую таблицу 'location'.

--country-- --city--
 France      Lyon
 Poland      Krakow
 France      Paris
 France      Marseille
 Italy       Milano

Теперь запрос

SELECT country FROM locations
GROUP BY country

приведет к:

--country--
 France
 Poland
 Italy

Тем не менее, следующий запрос

SELECT country, city FROM locations
GROUP BY country

... выдает ошибку в MS SQL, потому что как ваш компьютер может узнать, какой из трех французских городов "Лион", "Париж" или "Марсель" вы хотите прочитать в поле справа от "Франция"?

Чтобы исправить второй запрос, необходимо добавить эту информацию. Один из способов сделать это - использовать функции MAX () или MIN (), выбирая наибольшее или наименьшее значение среди всех кандидатов. MAX () и MIN () не только применимы к числовым значениям, но также сравнивают алфавитный порядок строковых значений.

SELECT country, MAX(city) FROM locations
GROUP BY country

приведет к:

--country-- --city--
 France      Paris
 Poland      Krakow
 Italy       Milano

или:

SELECT country, MIN(city) FROM locations
GROUP BY country

приведет к:

--country-- --city--
 France      Lyon
 Poland      Krakow
 Italy       Milano

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

Единственное решение, которое я смог найти, - это поместить весь ваш запрос в подзапрос и создать дополнительный столбец вне него руками:

SELECT
     countrylist.*,
     (SELECT TOP 1 city
     FROM locations
     WHERE
          country = countrylist.country
          AND city like 'M%'
     )
FROM
(SELECT country FROM locations
GROUP BY country) countrylist

приведет к:

--country-- --city--
 France      Marseille
 Poland      NULL
 Italy       Milano
Ульф Санне
источник
5

Отличный вопрос @aryaxt - вы можете сказать, что это был отличный вопрос, потому что вы задавали его 5 лет назад, и я наткнулся на него сегодня, пытаясь найти ответ!

Я только попытался отредактировать принятый ответ, чтобы включить это, но в случае, если мое редактирование не делает это в:

Если ваша таблица была не такой большой, и предполагая, что ваш первичный ключ представляет собой целое число с автоинкрементом, вы можете сделать что-то вроде этого:

SELECT 
  table.*
FROM table
--be able to take out dupes later
LEFT JOIN (
  SELECT field, MAX(id) as id
  FROM table
  GROUP BY field
) as noDupes on noDupes.id = table.id
WHERE
  //this will result in only the last instance being seen
  noDupes.id is not NULL
Гаррет Симпсон
источник
5

Пытаться

SELECT table.* FROM table 
WHERE otherField = 'otherValue'
GROUP BY table.fieldWantedToBeDistinct
limit x
Педро Рамос
источник
3

Вы можете сделать это с WITHоговоркой.

Например:

WITH c AS (SELECT DISTINCT a, b, c FROM tableName)
SELECT * FROM tableName r, c WHERE c.rowid=r.rowid AND c.a=r.a AND c.b=r.b AND c.c=r.c

Это также позволяет вам выбирать только строки, выбранные в WITHзапросе предложений.

user2225399
источник
2

Для SQL Server вы можете использовать diff_rank и дополнительные функции управления окнами, чтобы получить все строки И столбцы с дублированными значениями в указанных столбцах. Вот пример ...

with t as (
    select col1 = 'a', col2 = 'b', col3 = 'c', other = 'r1' union all
    select col1 = 'c', col2 = 'b', col3 = 'a', other = 'r2' union all
    select col1 = 'a', col2 = 'b', col3 = 'c', other = 'r3' union all
    select col1 = 'a', col2 = 'b', col3 = 'c', other = 'r4' union all
    select col1 = 'c', col2 = 'b', col3 = 'a', other = 'r5' union all
    select col1 = 'a', col2 = 'a', col3 = 'a', other = 'r6'
), tdr as (
    select 
        *, 
        total_dr_rows = count(*) over(partition by dr)
    from (
        select 
            *, 
            dr = dense_rank() over(order by col1, col2, col3),
            dr_rn = row_number() over(partition by col1, col2, col3 order by other)
        from 
            t
    ) x
)

select * from tdr where total_dr_rows > 1

При этом учитывается количество строк для каждой отдельной комбинации col1, col2 и col3.

dotjoe
источник
слишком сложный и специфичный для одной реализации SQL
Гаррет Симпсон
1
select min(table.id), table.column1
from table 
group by table.column1
KadoJ
источник
Это сработало для меня! Стоит отметить, что если вы используете fetch_array (), то вам нужно будет вызывать каждую строку с помощью индексной метки, а не неявно вызывать имя строки. Для этого недостаточно символов, чтобы написать пример, который у меня есть: X извините !!
Брэндон Принтисс
0
SELECT *
FROM tblname
GROUP BY duplicate_values
ORDER BY ex.VISITED_ON DESC
LIMIT 0 , 30

в ORDER BYя только что привел пример здесь, вы также можете добавить поле идентификатора в этом

SagarPPanchal
источник
Как уже упоминалось в принятом ответе, будет работать для большинства воплощений SQL - только для MYSQL
Гаррет Симпсон
0

Нашел это в другом месте здесь, но это простое решение, которое работает:

 WITH cte AS /* Declaring a new table named 'cte' to be a clone of your table */
 (SELECT *, ROW_NUMBER() OVER (PARTITION BY id ORDER BY val1 DESC) AS rn
 FROM MyTable /* Selecting only unique values based on the "id" field */
 )
 SELECT * /* Here you can specify several columns to retrieve */
 FROM cte
 WHERE rn = 1
Майкл Лихорадка
источник
Работает на MSSQL
Майкл Лихорадка
-1

Добавьте GROUP BY в поле, которое вы хотите проверить на дубликаты, ваш запрос может выглядеть

SELECT field1, field2, field3, ......   FROM table GROUP BY field1

field1 будет проверен, чтобы исключить повторяющиеся записи

или вы можете запросить как

SELECT *  FROM table GROUP BY field1

дубликаты записей field1 исключаются из SELECT

iCodeCrew
источник
1
Предложение GROUP BY должно соответствовать выбранным полям. иначе это выдаст ошибку какfiled2 must appear in the GROUP BY clause or be used in an aggregate function
Viuu -a
-2

Просто включите все свои поля в предложение GROUP BY.

wayneh
источник
3
Чтобы сделать это хорошим ответом, вы должны включить немного больше информации о том, что вы имеете в виду.
Роберт
-2

Это можно сделать внутренним запросом

$query = "SELECT * 
            FROM (SELECT field
                FROM table
                ORDER BY id DESC) as rows               
            GROUP BY field";
Захир Бабар
источник
2
Это не отвечает на вопрос, ОП пытался получить все данные таблицы, но удалил строки, содержащие дубликаты одного поля
Гаррет Симпсон
-3
SELECT * from table where field in (SELECT distinct field from table)
Эндрю
источник
7
Это не сделает работу. Вы выбрали отдельный столбец в подзапросе, но предложение where получает все эти столбцы с этим значением. Таким образом, запрос так же хорош, как запись «select * from table», если столбец «field» не является уникальным столбцом, и в этом случае различное значение для этого столбца не требуется вообще.
Анкур-м
-3

SELECT DISTINCT FIELD1, FIELD2, FIELD3 FROM TABLE1 работает, если значения всех трех столбцов являются уникальными в таблице.

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

Дорис Гамментхалер
источник
2
Это не отвечает на вопрос, ОП пытался получить все данные таблицы, но удалил строки, содержащие дубликаты одного поля
Гаррет Симпсон
-3

Я бы предложил использовать

SELECT  * from table where field1 in 
(
  select distinct field1 from table
)

таким образом, если у вас есть одно и то же значение в field1 в нескольких строках, будут возвращены все записи.

Иоаннис К
источник
1
Это не отличается с SELECT * FROM table;. Еще медленнее.
Шин Ким
Пожалуйста, попробуйте сначала ответить.
Шериф