Mysql
 sql >> Base de données >  >> RDS >> Mysql

MYSQL affiche des lignes incorrectes lors de l'utilisation de GROUP BY

C'est un obstacle classique auquel la plupart des programmeurs MySQL se heurtent.

  • Vous avez une colonne ticket_id c'est l'argument de GROUP BY . Des valeurs distinctes dans cette colonne définissent les groupes.
  • Vous avez une colonne incoming_time c'est l'argument de MAX() . La plus grande valeur de cette colonne sur les lignes de chaque groupe est renvoyée en tant que valeur de MAX() .
  • Vous avez toutes les autres colonnes du tableau article. Les valeurs renvoyées pour ces colonnes sont arbitraires, et ne proviennent pas de la même ligne où le MAX() valeur se produit.

La base de données ne peut pas déduire que vous voulez des valeurs de la même ligne où se trouve la valeur maximale.

Pensez aux cas suivants :

  • Il existe plusieurs lignes où la même valeur maximale se produit. Quelle ligne doit être utilisée pour afficher les colonnes de article.* ?

  • Vous écrivez une requête qui renvoie à la fois le MIN() et le MAX() . C'est légal, mais quelle ligne devrait article.* afficher ?

    SELECT article.* , MIN(article.incoming_time), MAX(article.incoming_time)
    FROM ticket, article
    WHERE ticket.id = article.ticket_id
    AND ticket.queue_id = 1
    GROUP BY article.ticket_id
    
  • Vous utilisez une fonction d'agrégation telle que AVG() ou SUM() , où aucune ligne n'a cette valeur. Comment la base de données peut-elle deviner quelle ligne afficher ?

    SELECT article.* , AVG(article.incoming_time)
    FROM ticket, article
    WHERE ticket.id = article.ticket_id
    AND ticket.queue_id = 1
    GROUP BY article.ticket_id
    

Dans la plupart des marques de bases de données - ainsi que dans le standard SQL lui-même - vous n'êtes pas autorisé pour écrire une requête comme celle-ci, à cause de l'ambiguïté. Vous ne pouvez pas inclure de colonne dans la liste de sélection qui ne se trouve pas dans une fonction d'agrégation ou nommée dans le GROUP BY clause.

MySQL est plus permissif. Il vous permet de le faire et vous laisse le soin d'écrire des requêtes sans ambiguïté. Si vous avez une ambiguïté, il sélectionne les valeurs de la ligne qui est physiquement la première dans le groupe (mais cela dépend du moteur de stockage).

Pour ce que ça vaut, SQLite a aussi ce comportement, mais il choisit le dernier rangée dans le groupe pour résoudre l'ambiguïté. Allez comprendre. Si la norme SQL ne dit pas quoi faire, c'est à l'implémentation du fournisseur de décider.

Voici une requête qui peut résoudre votre problème pour vous :

SELECT a1.* , a1.incoming_time AS maxtime
FROM ticket t JOIN article a1 ON (t.id = a1.ticket_id)
LEFT OUTER JOIN article a2 ON (t.id = a2.ticket_id 
  AND a1.incoming_time < a2.incoming_time)
WHERE t.queue_id = 1
  AND a2.ticket_id IS NULL;

En d'autres termes, recherchez une ligne (a1 ) pour lequel il n'y a pas d'autre ligne (a2 ) avec le même ticket_id et un incoming_time supérieur . S'il n'y a pas plus de incoming_time est trouvé, le LEFT OUTER JOIN renvoie NULL au lieu d'une correspondance.