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

MYSQL - Trier par identifiant dans l'ordre DESC, grouper par X

Vous avez mal compris le fonctionnement de GROUP BY dans SQL, en raison d'une fonctionnalité de MySQL. En SQL standard, chaque colonne non agrégée de l'instruction SELECT DOIT figurer dans la clause GROUP BY (il existe une exception pour les colonnes dont les valeurs dépendent à 100 % d'une colonne déjà dans la clause GROUP BY, bien que peu de variantes de SQL prennent en charge cette exemption) .

MySQL ne l'applique pas par défaut, mais les valeurs de lignes utilisées pour ces colonnes ne sont pas définies. Bien que vous puissiez obtenir celui que vous voulez, vous pourriez également ne pas le faire. Et même si vous le faites, il y a une chance que cela change à l'avenir.

L'ordre est normalement indépendant de GROUP BY, bien que si vous ne spécifiez pas de clause ORDER, les résultats seront ordonnés en fonction de ce qui était nécessaire pour effectuer le GROUPING (c'est-à-dire, si cela aide à ordonner les lignes dans un ordre pour faire le GROUP BY, MySQL ne prendra pas la peine de réorganiser les enregistrements par la suite, sauf si vous le lui dites spécifiquement avec une clause ORDER BY).

Ainsi, avec vos données actuelles, en regroupant par ads_post_id, la valeur de l'identifiant renvoyé pourrait être 22, 23, 24, 104, 250, 253 ou 767. Celui que MySQL choisit d'utiliser n'est pas défini.

Avec votre correction de données actuelle, cela est trivial car vous pouvez simplement obtenir l'identifiant MAX :-

SELECT ads_post_id, MAX(id) 
FROM fb_ads 
GROUP BY ads_post_id 
LIMIT 6

MAX renverra 1 ligne pour chaque valeur GROUPÉE.

Le problème normal est que les gens veulent une autre colonne pour cette ligne. Par exemple, supposons que chacune des lignes de votre exemple de données ait également une adresse IP et que vous souhaitiez celle qui équivaut à l'identifiant le plus élevé pour ads_post_id :-

id   | ads_post_id         ip_address
---------------------------------------------------------------------------
22   | 983314845117571     192.168.0.0
23   | 983314845117571     192.168.0.5
24   | 983314845117571     192.168.0.7    
104  | 983314845117571     192.168.0.0
250  | 983314845117571     192.168.0.4
253  | 983314845117571     192.168.0.6
767  | 983314845117571     192.168.0.1     
---------------------------------------------------------------------------

Dans ce cas, vous ne pouvez pas simplement utiliser MAX. Par exemple, si vous avez essayé :-

SELECT ads_post_id, MAX(id), MAX(ip_address) 
FROM fb_ads 
GROUP BY ads_post_id 
LIMIT 6

Vous obtiendrez les données suivantes renvoyées

id   | ads_post_id         ip_address
---------------------------------------------------------------------------
767  | 983314845117571     192.168.0.7     
---------------------------------------------------------------------------

Si vous essayez ce qui suit dans la plupart des versions de SQL, vous obtiendrez une erreur. Dans MySQL, avec les paramètres par défaut, vous obtiendriez un résultat, mais l'adresse IP renvoyée n'est pas définie (et en fait aléatoire).

SELECT ads_post_id, MAX(id), ip_address 
FROM fb_ads 
GROUP BY ads_post_id 
LIMIT 6

Les solutions à cela sont soit d'obtenir l'identifiant maximum pour chaque ads_post_id dans une sous-requête, puis de le joindre à la table pour obtenir le reste des valeurs :-

SELECT a.ads_post_id,
        a.id,
        a.ip_address
FROM fb_ads a
INNER JOIN
(
    SELECT ads_post_id, MAX(id) AS max_id 
    FROM fb_ads 
    GROUP BY ads_post_id 
) sub0
ON a.ads_post_id = sub0.ads_post_id
AND a.id = sub0.max_id

Une alternative consiste à (ab)utiliser la fonction d'agrégation GROUP_CONCAT. GROUP_CONCAT ramènera toutes les valeurs concaténées dans 1 champ, chacune séparée par un , (par défaut). Vous pouvez ajouter une clause ORDER BY pour forcer l'ordre dans lequel ils sont concaténés. Ensuite, vous pouvez utiliser SUBSTRING_INDEX pour tout renvoyer jusqu'à la première virgule.

Cela peut être utile pour des données simples, mais devient problématique avec des données textuelles ou des champs dont la valeur maximale est NULL.

SELECT a.ads_post_id,
        SUBSTRING_INDEX(GROUP_CONCAT(id ORDER BY id DESC), ',', 1),
        SUBSTRING_INDEX(GROUP_CONCAT(ip_address ORDER BY id DESC), ',', 1)
FROM fb_ads 
GROUP BY ads_post_id