web-dev-qa-db-fra.com

sélectionner toutes les lignes avec une valeur minimale

Dans Sqlite 3, j'essaie de comprendre comment sélectionner des lignes en fonction d'une valeur minimale. Je pense que je suis limité en ne connaissant pas suffisamment la terminologie associée pour rechercher efficacement sur Google.

Le tableau ressemble à:

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

Je veux obtenir les lignes où num2 est 1, 2, et 4. Je veux faire la sélection en fonction de la valeur minimale de num pour chaque valeur unique de la colonne de texte.

Donc pour text = 'a', la valeur minimale de numis 0, donc je veux les lignes 1 et 2. Pour text = 'b', la valeur minimale de num est 1, donc je veux la ligne 4.

En utilisant diverses combinaisons de group by, je peux obtenir les deux lignes 1 et 2 ou lignes 1 et 4. J'ai l'impression qu'il me manque un composant SQL qui ferait ce que je veux, mais je n'ai pas réussi à comprendre ce que cela pourrait être.

Quelle est la bonne façon de faire ce type de requête?

Solution possible

J'ai trouvé a moyen de le faire. Je ne suis pas assez réputé pour répondre à ma propre question, alors je fais la mise à jour ici. Je ne sais pas si c'est toujours correct ou à quoi ressemble l'efficacité. Tout commentaire est le bienvenu.

J'ai utilisé une instruction select composée, où une requête trouve la valeur minimale de num pour chaque valeur unique de texte:

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

Ensuite, je l'ai joint à la table complète pour obtenir toutes les lignes correspondant à ces deux colonnes.

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

Comme vous l'avez vu, un simple GROUP BY ne fonctionnera pas car il ne retournerait qu'un seul enregistrement par groupe.

Votre inscription fonctionne bien. Pour une grande table, elle ne sera efficace que s'il existe un index sur les colonnes de jointure (num et text).

Vous pouvez également utiliser une sous-requête corrélée:

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

SQLFiddle

Lors de son exécution, cette requête ne nécessite pas de table temporaire (votre requête le fait pour le résultat de u), mais exécutera la sous-requête pour chaque enregistrement dans t, donc text doit être indexé. (Ou utilisez un index à la fois sur text et num pour obtenir un indice de couverture .)

9
CL.

J'ai tendance à faire ce genre de chose avec une auto-jointure externe:

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

C'est essentiellement dire; donnez-moi tous les enregistrements qui n'ont pas une valeur plus élevée, c'est-à-dire null.

1
KISS

Alors, comment pouvez-vous trouver vous-même la réponse à votre question la prochaine fois? À mon avis, c'est en décomposant et en suivant la logique. Et vous avez raison:

Je veux faire la sélection en fonction de la valeur minimale de num pour chaque valeur unique de la colonne de texte

Cela se traduit par:

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

(Cela devrait être équivalent à votre requête having. Il pourrait être intéressant de jeter un œil aux lignes où num est égal à NULL. Soyez plus précis: jetez un œil aux effets des lignes avec des valeurs nulles , que vous voudrez peut-être filtrer en premier avec un where num is not null)

De là, vous pouvez obtenir le résultat souhaité en:

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

Ou en utilisant une jointure:

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.

Et lorsque les performances ne sont pas suffisantes pour vos tables, commencez à examiner des instructions plus complexes.

1
Grimaldi