SQL / mysql - Виберіть чіткий / УНІКАЛЬНИЙ, але повернути всі стовпці?


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

Я намагаюся виконати наступне твердження sql, але я хочу, щоб він повернув усі стовпці, чи це можливо? Щось на зразок:

SELECT DISTINCT field1, * from table

12
Чому це не SELECT DISTINCT * FROM tableпрацює для вас?
ypercubeᵀᴹ

19
Якщо у вашій таблиці є ПК, всі рядки мають бути distinctза визначенням. Якщо ви намагаєтеся просто вибрати, DISTINCT field1але якось повернути всі інші стовпці, що має відбутися для тих стовпців, у яких більше одного значення для певного field1значення? Вам потрібно буде використовувати, наприклад GROUP BY, агрегацію в інших стовпцях.
Мартін Сміт

1
Якщо ви хочете повторити рядки, а не лише окремі рядки, видаліть окреме ключове слово.
Гіперборей

2
Чи можете ви навести приклад того, як очікуєте виглядати результати? Поки я не можу зрозуміти потрібний запит.
рекурсивна

3
Ось відповідь на подібне запитання, що вам потрібно спочатку отримати окремий стовпець з їх ідентифікаторами, а потім з'єднати його з оригінальною таблицею.
ВИБІРТЕ ДИСТАНЦІЯ

Відповіді:


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
Chilianu Bogdan

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,,, не обов'язково бути цілими числами (або іншими цифрами), вони також можуть бути полями char
стебл

Гарно працював, поки я не застряг у булевій колоні. Значення стовпців MIN (Dynamic) змінюються на false, навіть якщо це було істинно. Будь-яка інша сукупна функція, доступна для адреси булевих - signonsridhar 6 хвилин тому. Сума (динамічна) змінилася помилковою на 1
синьонсрідхар

1
Чудова пропозиція, що привело мене до мого рішення, яке, на мою думку, є більш універсальним - подивіться!
Гаррет Сімпсон

@signonsridhar кидає ваш булевий код до int та використовує суму; наприкладsum(cast(COL as int)) > 0
Дрю

26

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

Якщо ви використовуєте GROUP BY без сукупної функції, яке поле, яке ви GROUP GROUP, буде вашим DISTINCT.

Якщо ви робите запит:

SELECT * from table GROUP BY field1;

Він покаже всі ваші результати на основі одного екземпляра поля1.

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

SELECT * FROM persons GROUP BY name;

Результатом буде те, що лише один екземпляр цього імені з’явиться з його адресою, а другий буде опущено з отриманої таблиці. Увага: якщо у ваших файлах є атомні значення, такі як firstName, lastName, ви хочете згрупувати їх обох.

SELECT * FROM persons GROUP BY lastName, firstName;

тому що якщо двоє людей мають одне прізвище, і ви згрупуєте лише прізвище, одна з цих осіб буде опущена з результатів. Вам потрібно враховувати ці речі. Сподіваюсь, це допомагає.


Як згадується у прийнятій відповіді, це спрацювало б для більшості втілень 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
Talha

2
Я вважаю, що це пов'язано з моїм використанням RedGate SQLPrompt. Те, як я налаштував це, завжди додає псевдоніми - навіть якщо це не потрібно. Це там "про всяк випадок"
Штормовий

Це виглядало для мене багатообіцяюче, але воно все ж повернуло всі ряди, а не окреме поле1. :(
Майкл Лихоманка

13

Це справді гарне запитання. Тут я вже прочитав кілька корисних відповідей, але, напевно, можу додати більш точне пояснення.

Зменшити кількість результатів запитів за допомогою оператора GROUP BY легко, якщо ви не запитуєте додаткову інформацію. Припустимо, ви отримали таку таблицю "Розташування".

--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


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запиті пропозицій.


2

Для SQL Server ви можете використовувати щільні_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.


занадто складний і специфічний для однієї реалізації SQL
Гаррет Сімпсон

1
select min(table.id), table.column1
from table 
group by table.column1

Це працювало для мене !! Варто відзначити тхо, якщо ви використовуєте fetch_array (), тоді вам потрібно буде викликати кожен рядок за допомогою мітки індексу, а не неявно називати ім'я рядка. У цьому мені недостатньо символів, щоб записати приклад, який я маю: Х вибачте !!
Brandon Printiss

0
SELECT *
FROM tblname
GROUP BY duplicate_values
ORDER BY ex.VISITED_ON DESC
LIMIT 0 , 30

в ORDER BYя просто покласти приклад тут, ви можете також додати поле ідентифікатора в цьому


Як згадується у прийнятій відповіді, це спрацювало б для більшості втілень 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
Michael Fever

-1

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

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

поле1 буде перевірено, щоб виключити повторювані записи

або ви можете запитувати, як

SELECT *  FROM table GROUP BY field1

дублікати записів field1 виключаються з SELECT


1
Становище GROUP BY повинно відповідати вибраним полям. в іншому випадку це призведе до помилки на кшталтfiled2 must appear in the GROUP BY clause or be used in an aggregate function
Viuu -

-2

Просто включіть усі свої поля до пункту GROUP BY.


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 OF TABLE1 працює, якщо значення всіх трьох стовпців є унікальними в таблиці.

Якщо, наприклад, у вас є кілька однакових значень для імені, але прізвище та інша інформація у вибраних стовпцях відрізняються, запис буде включений у набір результатів.


2
Це не відповідає на запитання, ОП намагалася отримати всі дані таблиці, але видалить рядки, що містять дублікати одного поля
Гаррет Сімпсон

-3

Я б запропонував використовувати

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

таким чином, якщо у вас є одне значення в field1 через кілька рядків, всі записи будуть повернуті.


1
З ним не відрізняється SELECT * FROM table;. Навіть більше Це повільно.
Шин Кім

Спробуйте спершу свою відповідь.
Шериф
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.