SQL / mysql - Wybierz odrębne / UNIKALNE, ale zwracają wszystkie kolumny?

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

Próbuję wykonać następującą instrukcję SQL, ale chcę, aby zwracała wszystkie kolumny. Czy to możliwe? Coś jak:

SELECT DISTINCT field1, * from table
aryaxt
źródło
12
Dlaczego SELECT DISTINCT * FROM tablenie działa dla ciebie?
ypercubeᵀᴹ
19
Jeśli twoja tabela ma PK, wszystkie wiersze powinny być distinctz definicji. Jeśli próbujesz tylko wybrać, DISTINCT field1ale jakoś zwrócić wszystkie inne kolumny, co powinno się stać dla tych kolumn, które mają więcej niż jedną wartość dla określonej field1wartości? Na przykład musisz użyć GROUP BYi pewnego rodzaju agregacji w innych kolumnach.
Martin Smith
1
Jeśli chcesz powtarzać wiersze, a nie tylko różne wiersze, usuń wyraźne słowo kluczowe.
Hyperboreus
2
Czy możesz podać przykład, w jaki sposób będą wyglądać wyniki? Jak dotąd nie mogę zrozumieć Twojego żądanego zapytania.
rekursywny
3
Oto odpowiedź na podobne pytanie, musisz najpierw uzyskać odrębną kolumnę z ich identyfikatorami, a następnie dołączyć ją do oryginalnej tabeli. WYBIERZ ODRÓŻNIENIE w jednej kolumnie, zwróć wiele innych kolumn
yadavr 21.04.13

Odpowiedzi:

407

Szukasz grupy według:

select *
from table
group by field1

Które czasami można napisać z wyraźnym oświadczeniem:

select distinct on field1 *
from table

Jednak na większości platform żadna z powyższych opcji nie będzie działać, ponieważ zachowanie innych kolumn nie jest określone. (Pierwszy działa w MySQL, jeśli tego używasz.)

Możesz pobrać odrębne pola i za każdym razem trzymać się wybierania jednego dowolnego wiersza.

Na niektórych platformach (np. PostgreSQL, Oracle, T-SQL) można to zrobić bezpośrednio za pomocą funkcji okna:

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

W innych (MySQL, SQLite) musisz napisać podkwerendy, dzięki którym dołączysz do całej tabeli ze sobą ( przykład ), więc nie jest to zalecane.

Denis de Bernardy
źródło
10
Zapytanie nie będzie analizować dla mnie i daje błąd: The ranking function "row_number" must have an ORDER BY clause. Musimy dodać kolejność według klauzuli po podziale według pola1. Tak więc prawidłowe zapytanie będzie 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
Dzięki! Miałem ten sam problem i rozwiązaniem było GROUP BY
Joaquin Iurchuk
2
Również w Oracle (Oracle SQL Developer) nie można określić select *, row_number() over (partition by field1 order by field2) as row_number from table. Musisz jawnie użyć nazwy tabeli / aliasu w wybranym zapytaniuselect **table**.*, row_number() over (partition by field1 order by field2) as row_number from table
meta4
1
@jarlh: Może być ... dzisiaj. Jak można zauważyć, ta odpowiedź ma prawie 7 lat, moment, w którym tak nie było, o ile mogę przypomnieć sobie, kiedy byłem aktywny. Możesz ponownie oznaczyć i / lub edytować odpowiedź, jeśli uważasz, że jest to konieczne.
Denis de Bernardy
2
select distinct on (field1) * from table; działa również w PostgreSQL
Chilianu Bogdan
61

Z frazowania twojego pytania rozumiem, że chcesz wybrać odrębne wartości dla danego pola i dla każdej takiej wartości mieć wszystkie pozostałe wartości kolumn w tym samym wierszu na liście. Większość DBMS nie pozwoli na to ani DISTINCTani GROUP BY, ponieważ wynik nie jest określony.

Pomyśl o tym w ten sposób: jeśli field1wystąpi więcej niż jeden raz, jaka wartość field2zostanie wyświetlona (biorąc pod uwagę, że masz taką samą wartość field1w dwóch wierszach, ale dwie różne wartości field2w tych dwóch wierszach).

Możesz jednak użyć funkcji agregujących (jawnie dla każdego pola, które chcesz wyświetlić) i użyć GROUP BYzamiast DISTINCT:

SELECT field1, MAX(field2), COUNT(field3), SUM(field4), .... FROM table GROUP BY field1
Costi Ciudatu
źródło
4
+1 za to rozwiązanie. Więc możemy to zrobić SELECT field1, MIN(field2), MIN(field3), MIN(field4), .... FROM table GROUP BY field1, a pola 2, 3, 4 ,,, nie muszą być liczbami całkowitymi (lub innymi cyframi), mogą to być również pola znakowe
prześledzić
Działał ładnie, dopóki nie utknąłem w kolumnie boolowskiej. Wartości kolumny MIN (dynamiczne) zostają zmienione na false, nawet jeśli to prawda. Każda inna funkcja agregująca dostępna do adresowania wartości logicznych - signonsridhar 6 minut temu. Suma (dynamiczna) zmieniono false na 1
signonsridhar
1
Świetna sugestia, doprowadziła mnie do mojego rozwiązania, które moim zdaniem jest bardziej uniwersalne - spójrz!
Garrett Simpson
@signonsridhar rzucił boolean na int i użył sumy; np.sum(cast(COL as int)) > 0
Drew
26

Jeśli dobrze zrozumiałem twój problem, jest podobny do tego, który właśnie miałem. Chcesz móc ograniczyć użyteczność DISTINCT do określonego pola, zamiast stosować go do wszystkich danych.

Jeśli użyjesz GROUP BY bez funkcji agregującej, w którymkolwiek polu GROUP GROUP będzie twoje DISTINCT.

Jeśli wykonasz zapytanie:

SELECT * from table GROUP BY field1;

Wyświetli wszystkie wyniki oparte na pojedynczej instancji field1.

Na przykład, jeśli masz tabelę z nazwą, adresem i miastem. Pojedyncza osoba ma zarejestrowanych wiele adresów, ale potrzebujesz tylko jednego adresu dla tej osoby, możesz zapytać w następujący sposób:

SELECT * FROM persons GROUP BY name;

Rezultat będzie taki, że tylko jedna instancja tej nazwy pojawi się z jej adresem, a druga zostanie pominięta w wynikowej tabeli. Uwaga: jeśli twoje pola mają wartości atomowe, takie jak imię, nazwisko, które chcesz pogrupować według obu.

SELECT * FROM persons GROUP BY lastName, firstName;

ponieważ jeśli dwie osoby mają to samo nazwisko, a ty grupujesz tylko według nazwiska, jedna z tych osób zostanie pominięta w wynikach. Musisz wziąć to pod uwagę. Mam nadzieję że to pomoże.

rocklandcitizen
źródło
Jak wspomniano w zaakceptowanej odpowiedzi, działałby dla większości wcieleń SQL - tylko dla MYSQL
Garrett Simpson
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
Burzliwy
źródło
Dlaczego jest czas, C aliaskiedy może działać bez niego? w kolejceFROM dbo.TABLE AS C
Talha,
2
Wierzę, że jest to spowodowane moim użyciem RedGate SQLPrompt. Sposób, w jaki go skonfigurowałem, zawsze dodaje aliasy - nawet jeśli nie jest to konieczne. Jest tam „na wszelki wypadek”
Stormy
Wyglądało to obiecująco, ale wciąż przywracało wszystkie wiersze, a nie wyraźne pole1. :(
Michael Fever
13

To naprawdę dobre pytanie. Przeczytałem już kilka przydatnych odpowiedzi tutaj, ale prawdopodobnie mogę dodać bardziej precyzyjne wyjaśnienie.

Zmniejszenie liczby wyników zapytań za pomocą instrukcji GROUP BY jest łatwe, o ile nie zapytasz o dodatkowe informacje. Załóżmy, że masz następującą tabelę „lokalizacje”.

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

Teraz zapytanie

SELECT country FROM locations
GROUP BY country

spowoduje:

--country--
 France
 Poland
 Italy

Jednak następujące zapytanie

SELECT country, city FROM locations
GROUP BY country

... zgłasza błąd w MS SQL, ponieważ skąd twój komputer może wiedzieć, które z trzech francuskich miast „Lyon”, „Paryż” lub „Marsylia” chcesz przeczytać w polu po prawej stronie „Francja”?

Aby poprawić drugie zapytanie, musisz dodać tę informację. Jednym ze sposobów jest skorzystanie z funkcji MAX () lub MIN (), wybierając największą lub najmniejszą wartość spośród wszystkich kandydatów. MAX () i MIN () mają zastosowanie nie tylko do wartości liczbowych, ale także porównują kolejność alfabetyczną wartości ciągów.

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

spowoduje:

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

lub:

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

spowoduje:

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

Te funkcje są dobrym rozwiązaniem, o ile nie przeszkadza ci wybranie wartości z obu końców kolejności alfabetycznej (lub numerycznej). Ale co, jeśli tak nie jest? Załóżmy, że potrzebujesz wartości o określonej charakterystyce, np. Zaczynającej się na literę „M”. Teraz sprawy się komplikują.

Jedynym rozwiązaniem, jakie do tej pory mogłem znaleźć, jest umieszczenie całego zapytania w podzapytaniu i zbudowanie dodatkowej kolumny poza nim ręcznie:

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

spowoduje:

--country-- --city--
 France      Marseille
 Poland      NULL
 Italy       Milano
Ulf Sanne
źródło
5

Świetne pytanie @aryaxt - można powiedzieć, że było to świetne pytanie, ponieważ zadałeś je 5 lat temu, a ja natknąłem się na to dzisiaj, próbując znaleźć odpowiedź!

Właśnie próbowałem edytować zaakceptowaną odpowiedź, aby uwzględnić to, ale w przypadku, gdy moja edycja nie zawiera:

Jeśli tabela nie była tak duża i zakładając, że klucz podstawowy jest liczbą całkowitą z automatyczną inkrementacją, możesz zrobić coś takiego:

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
Garrett Simpson
źródło
5

Próbować

SELECT table.* FROM table 
WHERE otherField = 'otherValue'
GROUP BY table.fieldWantedToBeDistinct
limit x
Pedro Ramos
źródło
3

Możesz to zrobić za pomocą WITHklauzuli.

Na przykład:

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

Pozwala to również wybrać tylko wiersze wybrane w WITHzapytaniu dotyczącym klauzul.

użytkownik2225399
źródło
2

W przypadku SQL Server można użyć dense_rank i dodatkowych funkcji okienkowania, aby uzyskać wszystkie wiersze ORAZ kolumny ze zduplikowanymi wartościami w określonych kolumnach. Oto przykład...

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

Pobiera to liczbę wierszy dla każdej odrębnej kombinacji col1, col2 i col3.

dotjoe
źródło
zbyt skomplikowane i specyficzne dla jednej implementacji SQL
Garrett Simpson
1
select min(table.id), table.column1
from table 
group by table.column1
KadoJ
źródło
To zadziałało dla mnie !! Warto zauważyć, że jeśli używasz fetch_array (), będziesz musiał wywoływać każdy wiersz za pomocą etykiety indeksu zamiast domyślnie wywoływać nazwę wiersza. Nie ma w tym wystarczająco dużo znaków, aby napisać przykład, który mam: X przepraszam !!
Brandon Printiss,
0
SELECT *
FROM tblname
GROUP BY duplicate_values
ORDER BY ex.VISITED_ON DESC
LIMIT 0 , 30

W ORDER BYwłaśnie podałem tutaj przykład, możesz również dodać pole ID w tym

SagarPPanchal
źródło
Jak wspomniano w zaakceptowanej odpowiedzi, działałby dla większości wcieleń SQL - tylko dla MYSQL
Garrett Simpson
0

Znalazłem to gdzie indziej tutaj, ale jest to proste rozwiązanie, które działa:

 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
Michael Fever
źródło
Działa dla MSSQL
Michael Fever
-1

Dodaj GROUP BY do pola, w którym chcesz sprawdzić, czy duplikaty mogą wyglądać Twoje zapytanie

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

pole1 zostanie zaznaczone, aby wykluczyć zduplikowane rekordy

lub możesz zapytać jak

SELECT *  FROM table GROUP BY field1

zduplikowane rekordy pola 1 są wykluczone z SELECT

iCodeCrew
źródło
1
Klauzula GROUP BY musi pasować do wybranych pól. inaczej wyrzuci błąd jakfiled2 must appear in the GROUP BY clause or be used in an aggregate function
Viuu -
-2

Wystarczy dołączyć wszystkie pola do klauzuli GROUP BY.

Wayneh
źródło
3
Aby była to dobra odpowiedź, powinieneś podać trochę więcej szczegółów na temat tego, co masz na myśli.
Robbert
-2

Można to zrobić za pomocą wewnętrznego zapytania

$query = "SELECT * 
            FROM (SELECT field
                FROM table
                ORDER BY id DESC) as rows               
            GROUP BY field";
Zaheer Babar
źródło
2
To nie odpowiada na pytanie, OP próbował uzyskać wszystkie dane z tabeli, ale usunął wiersze zawierające duplikaty jednego pola
Garrett Simpson
-3
SELECT * from table where field in (SELECT distinct field from table)
Andrzej
źródło
7
To nie zadziała. Wybrano odrębną kolumnę w podzapytaniu, ale klauzula where pobiera wszystkie kolumny o tej wartości. Tak więc zapytanie jest tak dobre, jak napisanie „wybierz * z tabeli”, chyba że kolumna „pole” jest unikalną kolumną, w którym to przypadku wyróżnienie w tej kolumnie nie jest wcale wymagane.
Ankur-m
-3

WYBIERZ POLE ODLEGŁOŚCI 1, FIELD2, FIELD3 Z TABELI 1 działa, jeśli wartości wszystkich trzech kolumn są unikalne w tabeli.

Jeśli na przykład masz wiele identycznych wartości dla imienia, ale nazwisko i inne informacje w wybranych kolumnach są różne, rekord zostanie uwzględniony w zestawie wyników.

Doris Gammenthaler
źródło
2
To nie odpowiada na pytanie, OP próbował uzyskać wszystkie dane z tabeli, ale usunął wiersze zawierające duplikaty jednego pola
Garrett Simpson
-3

Sugerowałbym użycie

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

w ten sposób, jeśli masz tę samą wartość w polu 1 w wielu wierszach, wszystkie rekordy zostaną zwrócone.

Ioannis K.
źródło
1
Nie inaczej jest z SELECT * FROM table;. Co więcej, jest wolny.
Shin Kim
Najpierw spróbuj swoją odpowiedź.
Sherif,