Come selezionare distinti per una colonna e uno in un'altra colonna?


29

Ho bisogno di interrogare un database SQL per trovare tutti i valori distinti di una colonna e ho bisogno di un valore arbitrario da un'altra colonna. Ad esempio, considera la seguente tabella con due colonne, chiave e valore:

key     value
===     =====
one     test
one     another
one     value
two     goes
two     here
two     also
three   example

Vorrei recuperare una riga campione, scelta arbitrariamente, da ciascuna chiave distinta, magari ottenendo queste tre righe:

key     value
===     =====
one     test
two     goes
three   example

Come posso formulare una query del genere in SQL?


2
Quale DBMS (Oracle, SQL-Server, DB2, MySQL, Postgres)?
ypercubeᵀᴹ

1
È un sistema proprietario.
WilliamKF,

Risposte:


33

La query più semplice da scrivere è per MySQL (con impostazioni ANSI non rigorose). Utilizza la costruzione non standard:

SELECT key, value
FROM tableX
GROUP BY key ;

Nella versione recente (5.7 e 8.0+) in cui le impostazioni rigide e ONLY_FULL_GROUP_BYsono quelle predefinite, è possibile utilizzare la ANY_VALUE()funzione, aggiunta in 5.7:

SELECT key, ANY_VALUE(value) AS value
FROM tableX
GROUP BY key ;

Per altri DBMS, che hanno funzioni di finestra (come Postgres, SQL-Server, Oracle, DB2), è possibile utilizzarli in questo modo. Il vantaggio è che puoi selezionare anche altre colonne nel risultato (oltre a keye value):

SELECT key, value
FROM tableX
    ( SELECT key, value,
             ROW_NUMBER() OVER (PARTITION BY key 
                                ORDER BY whatever)     --- ORDER BY NULL
               AS rn                                   --- for example
      FROM tableX
    ) tmp 
WHERE rn = 1 ;

Per le versioni precedenti di quanto sopra e per qualsiasi altro DBMS, un modo generale che funziona quasi ovunque. Uno svantaggio è che non è possibile selezionare altre colonne con questo approccio. Un altro è che funzioni aggregate come MIN()e MAX()non funzionano con alcuni tipi di dati in alcuni DBMS (come bit, testo, BLOB):

SELECT key, MIN(value) AS value
FROM tableX
GROUP BY key ;

PostgreSQL ha un DISTINCT ONoperatore speciale non standard che può anche essere usato. L'opzionale ORDER BYè per selezionare quale riga di ogni gruppo dovrebbe essere selezionata:

SELECT DISTINCT ON (key) key, value
FROM tableX
-- ORDER BY key, <some_other_expressions> ;

2
@WilliamKF Se per "scelto arbitrariamente" intendi "scelto a caso", sostituisci semplicemente la ORDER BY whateverquery in ypercube con una chiamata a una funzione per randomizzare i risultati.
Leigh Riffel,

1
@LeighRiffel Non deve essere casuale, nessuna scelta, semplice come il primo incontrato funziona bene.
WilliamKF,

3

Per MS-SQl Server:

;with FinalDataset as
(
    select *,
        row_number() over(partition by key order by value) as rownum
    from YourOriginalTable
)
select
   key,
   value
from FinalDataset 
where rownum = 1

Allo stesso modo, potresti avere rownum = 2 per il tuo secondo set di risultati


2

Simile alla risposta accettata, ma invece di min () o max () puoi usare array_agg ()

SELECT key, (array_agg(value))[1] AS value
FROM tableX
GROUP BY key ;

Se lo desideri, puoi ordinare i valori all'interno dell'array per selezionarne uno maggiore o minore:

SELECT key, (array_agg(value) ORDER BY value DESC)[1] AS value
FROM tableX
GROUP BY key ;

(controllato su PostgreSQL)

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.