Compter les doublons avec l’ORM Django

GROUP BY et HAVING sont assez peu intuitifs en SQL, et encore moins avec l’ORM Django.

Voici donc la recette pour compter le nombre d’occurrences des valeurs d’un champ, pour par exemple trouver les doublons.

En supposant une table Massage avec une valeur finish :

ID         FINISH
1          manuel
2          oral
3          oral
4          oral
>>> Massage.objects.values('finish').annotate(count=Count('id'))
{'manuel': 2, 'oral': '9': 'rectal': 1, 'paradoxal': -1}

Pour obtenir uniquement les doublons, il suffit de filtrer pour avoir les valeurs qui ont un count plus grand que 1 :

Massage.objects.values('finish').annotate(count=Count('id')).filter(count__gt=1)

No related posts.

flattr this!

11 comments

  1. SELECT DISTINCT ! ;-)

  2. foxmask

    le select distinct retourne qu’une occurence non ? là il veut les compter , ou j’ai pas compris.

    @sam : pour ma culture, ca donne quoi la requete SQL complete de tes 2 exemples ? peut-etre j’aurai une remarque en fonction de ca :)

  3. Le select distinct supprime les doublons, ma remarque concernait juste la fin de l’article…

  4. kikine

    SELECT COUNT(*) FROM Massage GROUP BY FINISH

  5. kikine

    ou mieux
    SELECT FINISH,COUNT(*) FROM Massage GROUP BY FINISH

  6. @bendem Le but n’est pas de supprimer les doublons, mais de les trouver.

    @kikine : pas sur, il faut regarder ce que l’ORM genère, ce n’est pas un humain.

    @foxmask, si j’y pense je regarderai.

  7. Zanguu

    @bendem, pour m’être régulièrement cassé le cul avec des requêtes à la con, si le GROUP BY est “peu intuitif” alors le DISTINCT j’en parle même pas. Et Il ne fait pas le travail d’un having comme le sous entend ton dernier message

    @foxmask, environ : Massage.objects.values('finish').annotate(total=Count('finish'))
    donne
    SELECT finish, count(*) 'total'
    FROM massage
    GROUP BY finish

    Et : Massage.objects.values('finish').annotate(total=Count('finish')).filter(total__gt=1)
    donne
    SELECT finish, count(*)
    FROM massage
    GROUP BY finish
    HAVING count(*) > 1

    Sachant qu’on peut apparement aussi utiliser :
    qs = Massage.objects.all()
    qs.query.group_by = ['finish']
    qs.query.having = ['count(finish) > 1']

    ps: en sql le count(*) et le count(finish) avec une clause GROUP BY reviennent (dans 99% des cas) au même

  8. Ah tiens, je découvre d’autres façons de l’écrire, moi je fais ça:
    SELECT finish, count(*) as c FROM massage GROUP BY finish HAVING c > 1

  9. Bon, on était pas loin, qs.query.sql_with_params() donne :

    SELECT "massage_massage"."finish", COUNT("massage_massage"."id") AS "count" FROM "massage_massage" GROUP BY "massage_massage"."finish"'
  10. zanguu

    dans le count, il faudrait un expert bdd pour savoir s’il y a une vraie différence de perf mais sur les résultats ça ne change pas grand chose d’y mettre un ‘*’ ou ‘finish’

    par contre tu as oublié de changer le nom de ta table dans le group by ? parce que là je vois pas bien d’où viens ce nom

  11. Oui, j’ai fais ça avec un projet en cours.

Flux RSS des commentaires

Leave a Reply

Your email address will not be published. Required fields are marked *

*

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong> <pre lang="" line="" escaped="" highlight="">

Jouer à mario en attendant que les autres répondent