2010-04-15 18 views
13

Czy istnieje prosty sposób, aby OGRANICZYĆ wyniki GROUP BY na górę 2. Następujące zapytanie zwraca wszystkie wyniki. Używanie "LIMIT 2" redukuje ogólną listę tylko do pierwszych 2 pozycji.MYSQL - Grupuj według limitów

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 

wyników w następujących przypadkach: -

 
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 

Dzięki Jon


Zgodnie z wnioskiem Mam załączeniu kopię struktury tabeli i niektórych danych testowych. Moim celem jest stworzenie jednolitego, że ma 2 najwyższych wyników z każdej unikalnej rating_name

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

Ponieważ 'GROUP BY' służy do wykonywania funkcji agregujących na powiązanych danych, jest to mało prawdopodobne. MySQL nie widzi 6 grup Polska, 4 Irlandia i 6 Francja, widzi 16 odrębnych grup bez żadnych powiązań. Co próbujesz osiągnąć? Być może jest inny sposób na grupowanie. – Duncan

+1

Czy możesz podać strukturę tabeli i niektóre dane testowe? Myślę, że istnieje możliwość zrobienia tego za pomocą HAVING i podkwerendy tam. –

Odpowiedz

10

Nie sądzę, że istnieje prosty sposób w MySQL. Jednym ze sposobów, aby to zrobić, jest wygenerowanie numeru wiersza dla każdego wiersza podzielonego na partycje według wartości oceny, a następnie wybieranie tylko tych wierszy z liczbą wierszy 2 lub mniejszą. W większości baz danych można zrobić przy użyciu coś takiego:

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 

Niestety, MySQL nie obsługuje składni ROW_NUMBER. Można jednak symulować ROW_NUMBER za pomocą zmiennych:

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 

Wynik po uruchomieniu na danych testowych:

 
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

Witam Draco - zaktualizowałem post, aby uwzględnić strukturę tabeli i dane. Doceniam wszystkie dotychczasowe opinie - dziękuję. – jono2010

4

To wciąż możliwe za pomocą jednego zapytania, ale to trochę długo, i istnieją pewne zastrzeżenia , które wyjaśnię po zapytaniu. Nie są to jednak błędy w zapytaniu, a niektóre niejasności w tym, co oznaczają "dwie pierwsze".

Oto zapytanie:

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 

Zastrzeżenie to, że jeśli jest więcej niż jeden id_market z tych samych „dobrych” liczą na tej samej rating_name, wtedy dostaniesz więcej niż dwa rekordy. Na przykład, jeśli istnieją trzy irlandzkie id_markets z "dobrą" liczbą 3, najwyższą, to jak możesz wyświetlić dwie najlepsze? Nie możesz. Tak więc zapytanie wyświetli wszystkie trzy.

Ponadto, jeśli było jedno policzenie "3", najwyższa i dwie liczby "2", nie można było wyświetlić dwóch pierwszych, ponieważ masz remis na drugie miejsce, więc zapytanie pokazuje wszystko trzy.

Zapytanie będzie prostsze, jeśli najpierw utworzysz tabelę tymczasową z agregowanym zestawem wyników, a następnie zaczniesz z niej pracować.

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

Po południu WSZYSTKO dzięki bardzo przemyślanej opinii - jest to bardzo cenne! Pozdrawiam Jon – jono2010

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

wyr1 może być jak CONCAT (...). Zaangażuj REPLACE, aby ukryć dowolny ";".

Powiązane problemy