2010-04-15 6 views
13

Existe-t-il un moyen simple de LIMITER les résultats GROUP BY au début 2. La requête suivante renvoie tous les résultats. L'utilisation de 'LIMIT 2' réduit la liste globale aux deux premières entrées seulement.MYSQL - Grouper par limite

select distinct(rating_name), 
     id_markets, 
     sum(rating_good) 'good', 
     sum(rating_neutral)'neutral', 
     sum(rating_bad) 'bad' 
from ratings 
where rating_year=year(curdate()) and rating_week= week(curdate(),1) 
group by rating_name,id_markets 
order by rating_name, sum(rating_good) 
desc 

Résultats dans les domaines suivants: -

 
poland 78 48 24 12 <- keep 
poland 1 15 5 0 <- keep 
poland 23 12 6 3 
poland 2 5 0 0 
poland 3 0 5 0 
poland 4 0 0 5 
ireland 1 9 3 0 <- keep 
ireland 2 3 0 0 <- keep 
ireland 3 0 3 0 
ireland 4 0 0 3 
france 12 24 12 6 <- keep 
france 1 3 1 0 <- keep 
france 231 1 0 0 
france 2 1 0 0 
france 4 0 0 1 
france 3 0 1 0 

Merci Jon


Comme l'a demandé, je joins une copie de la structure de la table et des données de test. Mon but est de créer une vue unique qui a les 2 meilleurs résultats de chaque rating_name uniques

CREATE TABLE `zzratings` (
    `id` int(11) NOT NULL AUTO_INCREMENT, 
    `id_markets` int(11) DEFAULT NULL, 
    `id_account` int(11) DEFAULT NULL, 
    `id_users` int(11) DEFAULT NULL, 
    `dateTime` timestamp NULL DEFAULT CURRENT_TIMESTAMP, 
    `rating_good` int(11) DEFAULT NULL, 
    `rating_neutral` int(11) DEFAULT NULL, 
    `rating_bad` int(11) DEFAULT NULL, 
    `rating_name` varchar(32) DEFAULT NULL, 
    `rating_year` smallint(4) DEFAULT NULL, 
    `rating_week` tinyint(4) DEFAULT NULL, 
    `cash_balance` decimal(9,6) DEFAULT NULL, 
    `cash_spend` decimal(9,6) DEFAULT NULL, 
    PRIMARY KEY (`id`), 
    KEY `rating_year` (`rating_year`), 
    KEY `rating_week` (`rating_week`), 
    KEY `rating_name` (`rating_name`) 
) ENGINE=MyISAM AUTO_INCREMENT=2166690 DEFAULT CHARSET=latin1; 

INSERT INTO `zzratings` (`id`,`id_markets`,`id_account`,`id_users`,`dateTime`,`rating_good`,`rating_neutral`,`rating_bad`,`rating_name`,`rating_year`,`rating_week`,`cash_balance`,`cash_spend`) 
VALUES 
    (63741, 1, NULL, 100, NULL, 1, NULL, NULL, 'poland', 2010, 15, NULL, NULL), 
    (63742, 1, NULL, 101, NULL, 1, NULL, NULL, 'poland', 2010, 15, NULL, NULL), 
    (1, 2, NULL, 102, NULL, 1, NULL, NULL, 'poland', 2010, 15, NULL, NULL), 
    (63743, 3, NULL, 103, NULL, NULL, 1, NULL, 'poland', 2010, 15, NULL, NULL), 
    (63744, 4, NULL, 104, NULL, NULL, NULL, 1, 'poland', 2010, 15, NULL, NULL), 
    (63745, 1, NULL, 105, NULL, 1, NULL, NULL, 'poland', 2010, 15, NULL, NULL), 
    (63746, 1, NULL, 106, NULL, NULL, 1, NULL, 'poland', 2010, 15, NULL, NULL), 
    (63747, 5, NULL, 100, NULL, 1, NULL, NULL, 'ireland', 2010, 15, NULL, NULL), 
    (63748, 5, NULL, 101, NULL, 1, NULL, NULL, 'ireland', 2010, 15, NULL, NULL), 
    (63749, 2, NULL, 102, NULL, 1, NULL, NULL, 'ireland', 2010, 15, NULL, NULL), 
    (63750, 3, NULL, 103, NULL, NULL, 1, NULL, 'ireland', 2010, 15, NULL, NULL), 
    (63751, 4, NULL, 104, NULL, NULL, NULL, 1, 'ireland', 2010, 15, NULL, NULL), 
    (63752, 1, NULL, 105, NULL, 1, NULL, NULL, 'ireland', 2010, 15, NULL, NULL), 
    (63753, 1, NULL, 106, NULL, NULL, 1, NULL, 'ireland', 2010, 15, NULL, NULL), 
    (63754, 1, NULL, 100, NULL, 1, NULL, NULL, 'ireland', 2010, 15, NULL, NULL), 
    (63755, 1, NULL, 101, NULL, 1, NULL, NULL, 'ireland', 2010, 15, NULL, NULL), 
    (63756, 2, NULL, 102, NULL, 1, NULL, NULL, 'ireland', 2010, 15, NULL, NULL), 
    (63757, 34, NULL, 103, NULL, NULL, 1, NULL, 'ireland', 2010, 15, NULL, NULL), 
    (63758, 34, NULL, 104, NULL, NULL, NULL, 1, 'ireland', 2010, 15, NULL, NULL), 
    (63759, 34, NULL, 105, NULL, 1, NULL, NULL, 'ireland', 2010, 15, NULL, NULL), 
    (63760, 34, NULL, 106, NULL, NULL, 1, NULL, 'ireland', 2010, 15, NULL, NULL), 
    (63761, 21, NULL, 100, NULL, 1, NULL, NULL, 'ireland', 2010, 15, NULL, NULL), 
    (63762, 21, NULL, 101, NULL, 1, NULL, NULL, 'ireland', 2010, 15, NULL, NULL), 
    (63763, 21, NULL, 102, NULL, 1, NULL, NULL, 'ireland', 2010, 15, NULL, NULL), 
    (63764, 21, NULL, 103, NULL, NULL, 1, NULL, 'ireland', 2010, 15, NULL, NULL), 
    (63765, 4, NULL, 104, NULL, NULL, NULL, 1, 'ireland', 2010, 15, NULL, NULL), 
    (63766, 1, NULL, 105, NULL, 1, NULL, NULL, 'ireland', 2010, 15, NULL, NULL), 
    (63767, 1, NULL, 106, NULL, NULL, 1, NULL, 'ireland', 2010, 15, NULL, NULL), 
    (63768, 1, NULL, 100, NULL, 1, NULL, NULL, 'france', 2010, 15, NULL, NULL), 
    (63769, 1, NULL, 101, NULL, 1, NULL, NULL, 'france', 2010, 15, NULL, NULL), 
    (63770, 2, NULL, 102, NULL, 1, NULL, NULL, 'france', 2010, 15, NULL, NULL), 
    (63771, 3, NULL, 103, NULL, NULL, 1, NULL, 'france', 2010, 15, NULL, NULL), 
    (63772, 4, NULL, 104, NULL, NULL, NULL, 1, 'france', 2010, 15, NULL, NULL); 
+0

Étant donné que 'GROUP BY' sert à effectuer des fonctions d'agrégation sur des données connexes, il est peu probable. MySQL ne voit pas 6 groupes en Pologne, 4 en Irlande et 6 en France, il voit 16 groupes distincts sans rien les relier. Qu'essayez-vous d'accomplir? Il y a peut-être une autre façon de faire le regroupement. – Duncan

+1

Pourriez-vous fournir la structure de la table et quelques données de test? Je pense qu'il y a une possibilité de faire cela en utilisant HAVING et sous-requête là-bas. –

Répondre

10

Je ne pense pas qu'il y ait un moyen simple de MySQL. Une façon de procéder est de générer un numéro de ligne pour chaque ligne partitionnée en groupes par rating_name, puis de sélectionner uniquement les lignes avec row_number 2 ou moins. Dans la plupart des bases de données, vous pouvez le faire en utilisant quelque chose comme:

SELECT * FROM (
    SELECT 
     rating_name, 
     etc..., 
     ROW_NUMBER() OVER (PARTITION BY rating_name ORDER BY good) AS rn 
    FROM your_table 
) T1 
WHERE rn <= 2 

Malheureusement, MySQL ne supporte pas la syntaxe ROW_NUMBER. Vous pouvez cependant simuler ROW_NUMBER en utilisant des variables:

SELECT 
    rating_name, id_markets, good, neutral, bad 
FROM (
    SELECT 
     *, 
     @rn := CASE WHEN @prev_rating_name = rating_name THEN @rn + 1 ELSE 1 END AS rn, 
     @prev_rating_name := rating_name 
    FROM (
     SELECT 
      rating_name, 
      id_markets, 
      SUM(COALESCE(rating_good, 0)) AS good, 
      SUM(COALESCE(rating_neutral, 0)) AS neutral, 
      SUM(COALESCE(rating_bad, 0)) AS bad 
     FROM zzratings 
     WHERE rating_year = YEAR(CURDATE()) AND rating_week = WEEK(CURDATE(), 1) 
     GROUP BY rating_name, id_markets 
    ) AS T1, (SELECT @prev_rating_name := '', @rn := 0) AS vars 
    ORDER BY rating_name, good DESC 
) AS T2 
WHERE rn <= 2 
ORDER BY rating_name, good DESC 

Résultat lorsqu'il est exécuté sur vos données de test:

 
france 1 2 0 0 
france 2 1 0 0 
ireland 1 4 2 0 
ireland 21 3 1 0 
poland 1 3 1 0 
poland 2 1 0 0 
+0

Bonjour Draco - J'ai mis à jour le post pour inclure la structure de la table et les données. J'apprécie tous les commentaires jusqu'à présent - merci. – jono2010

4

Ceci est encore possible via une seule requête, mais il est un peu long, et il y a des mises en garde , que je vais expliquer après la requête. Cependant, ils ne sont pas des failles dans la requête, mais plutôt une certaine ambiguïté dans ce que «top deux» signifie.

est ici la requête:

SELECT ratings.* FROM 
(SELECT rating_name, 
     id_markets, 
     sum(rating_good) 'good', 
     sum(rating_neutral)'neutral', 
     sum(rating_bad) 'bad' 
FROM zzratings 
WHERE rating_year=year(curdate()) AND rating_week = week(curdate(),1) 
GROUP BY rating_name,id_markets) AS ratings 
LEFT JOIN 
(SELECT rating_name, 
     id_markets, 
     sum(rating_good) 'good', 
     sum(rating_neutral)'neutral', 
     sum(rating_bad) 'bad' 
FROM zzratings 
WHERE rating_year=year(curdate()) AND rating_week= week(curdate(),1) 
GROUP BY rating_name,id_markets) AS ratings2 
ON ratings2.good <= ratings.good AND 
    ratings2.id_markets <> ratings.id_markets AND 
    ratings2.rating_name = ratings.rating_name 
LEFT JOIN 
(SELECT rating_name, 
     id_markets, 
     sum(rating_good) 'good', 
     sum(rating_neutral)'neutral', 
     sum(rating_bad) 'bad' 
FROM zzratings 
WHERE rating_year=year(curdate()) AND rating_week= week(curdate(),1) 
GROUP BY rating_name,id_markets) AS ratings3 
ON ratings3.good >= ratings2.good AND 
    ratings3.id_markets <> ratings.id_markets AND 
    ratings3.id_markets <> ratings2.id_markets AND 
    ratings3.rating_name = ratings.rating_name 
WHERE (ratings2.good IS NULL OR ratings3.good IS NULL) AND 
    ratings.good IS NOT NULL 
ORDER BY ratings.rating_name, ratings.good DESC 

La mise en garde est que s'il y a plus d'un id_market avec le même nombre de « bons » pour la même rating_name, alors vous obtiendrez plus de deux dossiers. Par exemple, s'il y a trois id_markets ireland avec un "bon" nombre de 3, le plus élevé, alors comment pouvez-vous afficher les deux premiers? Tu ne peux pas. Donc, la requête montrera tous les trois. En outre, s'il y avait un compte de "3", le plus élevé, et deux comptes de "2", vous ne pouviez pas afficher les deux premiers, puisque vous avez une égalité pour la deuxième place, de sorte que la requête montre tous Trois.

La requête sera plus simple si vous créez d'abord une table temporaire avec le jeu de résultats agrégé, puis travaillez à partir de celle-ci.

CREATE TEMPORARY TABLE temp_table 
    SELECT rating_name, 
      id_markets, 
      sum(rating_good) 'good', 
      sum(rating_neutral)'neutral', 
      sum(rating_bad) 'bad' 
    FROM zzratings 
    WHERE rating_year=year(curdate()) AND rating_week= week(curdate(),1; 

SELECT ratings.* 
FROM temp_table ratings 
LEFT JOIN temp_table ratings2 
ON ratings2.good <= ratings.good AND 
    ratings2.id_markets <> ratings.id_markets AND 
    ratings2.rating_name = ratings.rating_name 
LEFT JOIN temp_table ratings3 
ON ratings3.good >= ratings2.good AND 
    ratings3.id_markets <> ratings.id_markets AND 
    ratings3.id_markets <> ratings2.id_markets AND 
    ratings3.rating_name = ratings.rating_name 
WHERE (ratings2.good IS NULL OR ratings3.good IS NULL) AND 
    ratings.good IS NOT NULL 
ORDER BY ratings.rating_name, ratings.good DESC; 
+0

Après-midi tous, merci beaucoup les commentaires très réfléchis - c'est très apprécié! Cordialement Jon – jono2010

0
SUBSTRING_INDEX(
    GROUP_CONCAT(expr1 ORDER BY expr2 SEPARATOR ";"), 
    ";", 
    2 /* the GROUP_LIMIT */ 
) 

Expr1 peut être comme CONCAT (...). Impliquer REPLACE pour masquer tout ";".