seleziona tutte le righe con un valore minimo


9

In Sqlite 3 sto cercando di capire come selezionare le righe in base a un valore minimo. Penso di essere limitato dal non conoscere abbastanza la terminologia correlata per cercare efficacemente su Google.

La tabella si presenta come:

num         text        num2      
----------  ----------  ----------
0           a           1         
0           a           2         
1           a           3         
1           b           4         

Voglio ottenere le righe in cui num2è 1, 2, e 4. Voglio fare la selezione in base al valore minimo di num per ogni valore univoco della colonna di testo.

Quindi, per text = 'a', il valore minimo di numè 0, quindi voglio le righe 1 e 2. Per text = 'b', il valore minimo di numè 1, quindi voglio la riga 4.

Utilizzando varie combinazioni di gruppo da parte, io sono in grado di ottenere sia le righe 1e 2righe o 1e 4. Mi sento come se mi mancasse un componente SQL che farebbe quello che voglio, ma non sono stato in grado di capire cosa potrebbe essere.

Qual è il modo corretto di eseguire questo tipo di query?

Possibile soluzione

Ho trovato un modo per farlo. Non sono abbastanza affidabile per rispondere alla mia domanda, quindi sto facendo l'aggiornamento qui. Non sono sicuro che sia sempre corretto o come sia l'efficienza. Tutti i commenti sono benvenuti

Ho usato un'istruzione di selezione composta, in cui una query trova il valore minimo di num per ogni valore univoco di testo:

sqlite> select num, text from t group by text having num = min( num );
num         text      
----------  ----------
0           a         
1           b         

Quindi ho unito questo con la tabella completa per ottenere tutte le righe corrispondenti a queste due colonne.

sqlite> with u as
      ( select num, text from t group by text having num = min( num ) )
        select t.* from t join u on t.num = u.num and t.text = u.text;
num         text        num2      
----------  ----------  ----------
0           a           1         
0           a           2         
1           b           4         

Risposte:


10

Come hai visto, un semplice GROUP BY non funzionerà perché restituirebbe solo un record per gruppo.

Il tuo join funziona bene. Per una tabella di grandi dimensioni, sarà efficace solo se è presente un indice sulle colonne di join ( nume text).

In alternativa, è possibile utilizzare una sottoquery correlata:

SELECT *
FROM t
WHERE num = (SELECT MIN(num)
             FROM t AS t2
             WHERE t2.text = t.text);

SQLFiddle

Quando viene eseguita, questa query non richiede una tabella temporanea (la query fa per il risultato di u), ma eseguirà la subquery per ciascun record in t, quindi textdovrebbe essere indicizzata. (Oppure usa un indice su entrambi texte numper ottenere un indice di copertura .)


non ha alcuna tabella temporanea nella sua query, solo un CTE, che è abbastanza diverso.
ypercubeᵀᴹ

Quando eseguito, il risultato della uquery viene archiviato in una tabella temporanea, indipendentemente dal fatto che sia scritto come CTE, vista o in linea come sottoquery.
CL.

Grazie, questa versione è molto più facile da scrivere rispetto a quella in cui mi sono imbattuto. Conoscere la terminologia giusta è anche utile per me approfondire questo aspetto.
user35292

@CL È così che SQLite esegue le query con CTE? Hai un riferimento per questo? Perché altri DBMS non usano necessariamente tabelle temporanee per cte.
ypercubeᵀᴹ

I CTE, le viste e le sottoquery di @ypercube sono appiattiti o implementati come coroutine, se possibile. Ma un GROUP BY su una colonna non indicizzata deve essere in grado di raccogliere i dati per tutti i gruppi in parallelo, quindi richiede una forma di tabella temporanea (in tutti i database).
CL.

1

Tendo a fare questo tipo di cose con un self self join:

SELECT
    M1.Num,
    M1.Text,
    M1.Num2
FROM
    MyDb M1
LEFT OUTER JOIN
    MyDB M2
ON
    M1.text = M2.text
AND
    M1.num > m2.num
WHERE
    M2.num is null

Questo sta sostanzialmente dicendo; dammi tutti i record che non hanno un valore più alto, cioè null.


1

Quindi, come puoi trovare tu stesso la risposta alla tua domanda la prossima volta? A mio avviso, si tratta di decomporre e seguire la logica. E hai capito bene:

Voglio fare la selezione in base al valore minimo di num per ogni valore univoco della colonna di testo

Questo si traduce in:

select text, min(num) from t group by text;

(Questo dovrebbe essere equivalente alla tua havingquery. Potrebbe essere interessante dare un'occhiata alle righe dove è numuguale a NULL. Per essere più precisi: dai un'occhiata all'effetto delle righe con valori nulli, che potresti voler filtrare per primo con un where num is not null)

Da qui è possibile ottenere il risultato desiderato:

select * from t where (num, text) in ( *insert query above* )

O usando un join:

select t1.* from t t1,
    (select text, min(num) as n from t group by text) t2
where t1.num = t2.n and t1.text = t2.text.

E quando le prestazioni non sono sufficienti per le tue tabelle, inizia a guardare istruzioni più complesse.


-2

Questa query non dovrebbe essere esattamente ciò di cui hai bisogno?

select min(num), text, num2 group by text, num2

Ciò restituirà tutti e quattro i record, perché i num2valori sono univoci.
CL.
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.