SQL / mysql - Seleziona distinto / UNICO ma restituisce tutte le colonne?


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

Sto cercando di realizzare la seguente istruzione sql ma voglio che restituisca tutte le colonne è possibile? Qualcosa di simile a:

SELECT DISTINCT field1, * from table

12
Perché SELECT DISTINCT * FROM tablenon funziona per te?
ypercubeᵀᴹ

19
Se la tua tabella ha un PK, tutte le righe dovrebbero essere distinctper definizione. Se stai provando a selezionare DISTINCT field1ma in qualche modo restituire tutte le altre colonne, cosa dovrebbe succedere per quelle colonne che hanno più di un valore per un field1valore particolare ? Dovresti usare, GROUP BYper esempio, una sorta di aggregazione sulle altre colonne.
Martin Smith,

1
Se vuoi righe ripetute e non solo righe distinte, rimuovi la parola chiave distinta.
Iperboro,

2
Potresti dare un esempio di come ti aspetti i risultati? Finora, non riesco a dare alcun senso alla query desiderata.
ricorsivo

3
Ecco la risposta a una domanda simile, devi prima ottenere la colonna distinta con i loro ID e poi unirla con la tabella originale. SELEZIONA DISTINCT su una colonna, restituisce più altre colonne
yadavr

Risposte:


407

Stai cercando un gruppo per:

select *
from table
group by field1

Che a volte può essere scritto con una dichiarazione on distinta:

select distinct on field1 *
from table

Sulla maggior parte delle piattaforme, tuttavia, nessuna delle precedenti funzionerà perché il comportamento sulle altre colonne non è specificato. (Il primo funziona in MySQL, se è quello che stai usando.)

È possibile recuperare i campi distinti e attenersi alla selezione di una singola riga arbitraria ogni volta.

Su alcune piattaforme (ad esempio PostgreSQL, Oracle, T-SQL) questo può essere fatto direttamente usando le funzioni della finestra:

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

Su altri (MySQL, SQLite), dovrai scrivere sottoquery che ti faranno unire l'intera tabella con se stessa ( esempio ), quindi non è raccomandato.


10
La query non analizzare per me e dà un errore: The ranking function "row_number" must have an ORDER BY clause. Dobbiamo aggiungere la clausola order by dopo la partizione per field1. Quindi la query corretta sarà 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
Grazie! Ero nello stesso problema e la soluzione era GROUP BY
Joaquin Iurchuk,

2
Anche in Oracle (Oracle SQL Developer) non è possibile specificare select *, row_number() over (partition by field1 order by field2) as row_number from table. Devi usare esplicitamente il nome / alias della tabella nella query selezionataselect **table**.*, row_number() over (partition by field1 order by field2) as row_number from table
meta4

1
@jarlh: Potrebbe essere ... oggi. Come puoi notare, questa risposta ha quasi 7 anni, un momento nel quale non era il caso nella misura in cui posso ricordare da quando ero attivo. Puoi ritaginare e / o modificare la risposta se ritieni che sia necessario.
Denis de Bernardy,

2
select distinct on (field1) * from table; funziona anche in PostgreSQL
Chilianu Bogdan,

61

Dal fraseggio della tua domanda, capisco che vuoi selezionare i valori distinti per un determinato campo e per ciascuno di tali valori avere tutti gli altri valori di colonna nella stessa riga elencata. La maggior parte DBMS non permetterà questo con nessuno DISTINCT, né GROUP BY, perché il risultato non è determinato.

Pensala in questo modo: se ti presenti field1più di una volta, quale valore di field2sarà elencato (dato che hai lo stesso valore per field1in due righe ma due valori distinti field2in in quelle due righe).

Puoi comunque usare le funzioni aggregate (esplicitamente per ogni campo che vuoi mostrare) e usando un GROUP BYinvece di DISTINCT:

SELECT field1, MAX(field2), COUNT(field3), SUM(field4), .... FROM table GROUP BY field1

4
+1 per questa soluzione. Quindi possiamo fare SELECT field1, MIN(field2), MIN(field3), MIN(field4), .... FROM table GROUP BY field1, e field2, 3, 4 ,,, non devono essere numeri interi (o altre cifre), possono anche essere campi di caratteri
gambo

Funzionava bene fino a quando non rimasi bloccato in una colonna booleana. I valori di colonna MIN (dinamico) vengono modificati su false anche se fosse vero. Qualsiasi altra funzione aggregata disponibile per indirizzare booleana - signonsridhar 6 minuti fa. Somma (dinamica) modificata da 1 a 1
signonsridhar

1
Un grande suggerimento, mi ha portato alla mia soluzione che penso sia più universale - dai un'occhiata!
Garrett Simpson,

@signonsridhar lancia il tuo booleano in un int e usa la somma; ad esempiosum(cast(COL as int)) > 0
Drew

26

Se ho capito correttamente il tuo problema, è simile a quello che ho appena avuto. Volete essere in grado di limitare l'usabilità di DISTINCT a un campo specificato, piuttosto che applicarlo a tutti i dati.

Se si utilizza GROUP BY senza una funzione di aggregazione, il campo DISTINCT archiviato in qualsiasi momento GROUP BY sarà archiviato.

Se fai la tua richiesta:

SELECT * from table GROUP BY field1;

Mostrerà tutti i risultati in base a una singola istanza di field1.

Ad esempio, se hai una tabella con nome, indirizzo e città. Una singola persona ha più indirizzi registrati, ma vuoi solo un singolo indirizzo per la persona, puoi fare una query come segue:

SELECT * FROM persons GROUP BY name;

Il risultato sarà che verrà visualizzata solo un'istanza di quel nome con il suo indirizzo e l'altra verrà omessa dalla tabella risultante. Attenzione: se i tuoi file hanno valori atomici come firstName, lastName che vuoi raggruppare per entrambi.

SELECT * FROM persons GROUP BY lastName, firstName;

perché se due persone hanno lo stesso cognome e si raggruppa solo per cognome, una di quelle persone verrà omessa dai risultati. Devi tenere in considerazione queste cose. Spero che sia di aiuto.


Come menzionato nella risposta accettata, avrebbe funzionato per la maggior parte delle incarnazioni di SQL - solo per MYSQL
Garrett Simpson il

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

Perché c'è C aliasquando può funzionare senza di essa? in lineaFROM dbo.TABLE AS C
Talha,

2
Credo che ciò sia dovuto al mio uso di RedGate SQLPrompt. Il modo in cui l'ho configurato, aggiunge sempre alias, anche se non necessario. È lì "per ogni evenienza"
Stormy il

Mi è sembrato promettente, ma ha comunque riportato tutte le righe, non il campo distinto1. :(
Michael Fever il

13

Questa è davvero una bella domanda. Ho già letto alcune risposte utili qui, ma probabilmente posso aggiungere una spiegazione più precisa.

Ridurre il numero di risultati della query con un'istruzione GROUP BY è facile purché non si richiedano informazioni aggiuntive. Supponiamo che tu abbia la seguente tabella 'posizioni'.

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

Ora la domanda

SELECT country FROM locations
GROUP BY country

comporterà:

--country--
 France
 Poland
 Italy

Tuttavia, la seguente query

SELECT country, city FROM locations
GROUP BY country

... genera un errore in MS SQL, perché come fa il tuo computer a sapere quale delle tre città francesi "Lione", "Parigi" o "Marsiglia" vuoi leggere nel campo a destra di "Francia"?

Per correggere la seconda query, è necessario aggiungere queste informazioni. Un modo per farlo è utilizzare le funzioni MAX () o MIN (), selezionando il valore più grande o più piccolo tra tutti i candidati. MAX () e MIN () non si applicano solo ai valori numerici, ma confrontano anche l'ordine alfabetico dei valori di stringa.

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

comporterà:

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

o:

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

comporterà:

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

Queste funzioni sono una buona soluzione purché tu stia bene selezionando il tuo valore da entrambe le estremità dell'ordine alfabetico (o numerico). E se non fosse così? Supponiamo che tu abbia bisogno di un valore con una certa caratteristica, ad esempio iniziando con la lettera "M". Ora le cose si complicano.

L'unica soluzione che ho trovato finora è mettere l'intera query in una sottoquery e costruire manualmente la colonna aggiuntiva al di fuori di essa:

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

comporterà:

--country-- --city--
 France      Marseille
 Poland      NULL
 Italy       Milano

5

Grande domanda @aryaxt - puoi dire che è stata una bella domanda perché l'hai fatta 5 anni fa e oggi mi sono imbattuto in esso cercando di trovare la risposta!

Ho appena provato a modificare la risposta accettata per includerla, ma nel caso in cui la mia modifica non la includa:

Se la tua tabella non era così grande e supponendo che la tua chiave primaria fosse un numero intero auto-incrementante, potresti fare qualcosa del genere:

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

Provare

SELECT table.* FROM table 
WHERE otherField = 'otherValue'
GROUP BY table.fieldWantedToBeDistinct
limit x

3

Puoi farlo con a WITH clausola.

Per esempio:

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

Ciò consente anche di selezionare solo le righe selezionate nella WITHquery delle clausole.


2

Per SQL Server è possibile utilizzare dense_rank e funzioni di finestre aggiuntive per ottenere tutte le righe E le colonne con valori duplicati su colonne specificate. Ecco un esempio ...

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

Questo richiede un conteggio delle righe per ogni combinazione distinta di col1, col2 e col3.


troppo complicato e specifico per un'implementazione di SQL
Garrett Simpson

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

Questo ha funzionato per me !! Vale la pena notare che, se si utilizza fetch_array (), sarà necessario chiamare ogni riga tramite un'etichetta di indice anziché chiamare in modo implicito il nome della riga. Non ci sono abbastanza personaggi in questo per me per scrivere l'esempio che ho: X mi dispiace !!
Brandon Printiss,

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

in ORDER BYho appena fatto un esempio qui, puoi anche aggiungere un campo ID in questo


Come menzionato nella risposta accettata, avrebbe funzionato per la maggior parte delle incarnazioni di SQL - solo per MYSQL
Garrett Simpson il

0

Trovato qui altrove ma questa è una soluzione semplice che funziona:

 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

Funziona con MSSQL
Michael Fever il

-1

Aggiungi GROUP BY al campo in cui desideri verificare la presenza di duplicati della query

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

campo1 verrà controllato per escludere record duplicati

o puoi fare una query come

SELECT *  FROM table GROUP BY field1

i record duplicati di field1 sono esclusi da SELECT


1
La clausola GROUP BY deve corrispondere ai campi selezionati. altrimenti genererà un errore comefiled2 must appear in the GROUP BY clause or be used in an aggregate function
Viuu -a

-2

Includi tutti i tuoi campi nella clausola GROUP BY.


3
Per rendere questa una buona risposta, dovresti includere un po 'più di dettaglio su cosa intendi.
Robbert,

-2

Può essere fatto tramite query interna

$query = "SELECT * 
            FROM (SELECT field
                FROM table
                ORDER BY id DESC) as rows               
            GROUP BY field";

2
Questo non risponde alla domanda, l'OP stava cercando di ottenere tutti i dati della tabella ma rimuoveva le righe contenenti i duplicati di un singolo campo
Garrett Simpson,

-3
SELECT * from table where field in (SELECT distinct field from table)

7
Questo non farà il lavoro. Hai selezionato la colonna distinta nella sottoquery ma la clausola where ottiene tutte quelle colonne con quel valore. Quindi la query è valida quanto scrivere 'select * from table' a meno che la colonna 'field' sia una colonna unica nel qual caso il distinto su quella colonna non è affatto richiesto.
Ankur-m,

-3

SELECT DISTINCT FIELD1, FIELD2, FIELD3 FROM TABLE1 funziona se i valori di tutte e tre le colonne sono univoci nella tabella.

Se, ad esempio, hai più valori identici per il nome, ma il cognome e altre informazioni nelle colonne selezionate sono diversi, il record verrà incluso nel set di risultati.


2
Questo non risponde alla domanda, l'OP stava cercando di ottenere tutti i dati della tabella ma rimuoveva le righe che contenevano i duplicati di un singolo campo
Garrett Simpson,

-3

Suggerirei di usare

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

in questo modo se si ha lo stesso valore in field1 su più righe, verranno restituiti tutti i record.


1
Non è diverso con SELECT * FROM table;. Ancora di più È lento.
Shin Kim,

Per favore, prova prima la tua risposta.
Sherif,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.