2010-04-15 3 views
13

GROUP BY 결과를 상위 2로 제한하는 간단한 방법이 있습니까? 다음 쿼리는 모든 결과를 반환합니다. 'LIMIT 2'를 사용하면 전체 목록이 상위 2 개 항목으로 만 줄어 듭니다. 다음에MYSQL - 제한에 의한 그룹

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 

결과 : - 나는 테이블 구조와 테스트 데이터의 사본을 첨부 요청으로

 
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 

감사 존


. 내 목표는 각 고유 한 등급에서 상위 2 개의 결과를 갖는 단일보기를 만드는 것입니다.

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

'GROUP BY'는 관련 데이터에 대해 집계 함수를 수행하기위한 것이기 때문에 가능성은 희박합니다. MySQL은 6 개의 폴란드, 4 개의 아일랜드, 6 개의 프랑스 그룹을 보지 않으며 16 개의 별개의 그룹과 관련이 없습니다. 무엇을 성취하려고합니까? 그룹화를 수행하는 또 다른 방법이있을 수 있습니다. – Duncan

+1

테이블 구조와 일부 테스트 데이터를 제공 할 수 있습니까? HAVING과 하위 쿼리를 사용하여이 작업을 수행 할 수있는 가능성이 있다고 생각합니다. –

답변

10

나는 MySQL에 간단한 방법이 있다고 생각하지 않습니다. 이 작업을 수행하는 한 가지 방법은 rating_name으로 그룹화 된 각 행에 행 번호를 생성 한 다음 row_number가 2 이하인 행만 선택하는 것입니다.

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 

불행하게도, MySQL은이 ROW_NUMBER 구문을 지원하지 않습니다 대부분의 데이터베이스에서 당신은 같이 사용하여이 일을 할 수 있습니다. 테스트 데이터를 실행할 때

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 

결과 : 당신은 그러나 ROW_NUMBER 사용하여 변수를 시뮬레이션 할 수 있습니다

 
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

안녕하세요 Draco - 게시글을 업데이트하여 테이블 구조와 데이터를 포함 시켰습니다. 지금까지의 모든 의견에 감사드립니다. - 감사합니다. – jono2010

4

이 단일 쿼리를 통해 여전히 가능하지만, 조금 긴, 그리고 몇 가지주의 사항이있다 , 나는 질의 후에 설명 할 것이다. 비록 "상위 2"가 의미하는 것의 모호성만큼 질문의 결함이 아닙니다.

다음은 쿼리 :

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 

주의해야 할 점은 그 같은 "좋은"당신이 얻을 것이다, 같은 rating_name에 대한 카운트 두 개 이상의 레코드가 하나 이상의 id_market가있는 경우. 예를 들어 3 개의 "좋은"개수의 id_markets가있는 경우, 가장 높은 값을 가진 id_markets가 3 개있는 경우 상위 2 개를 어떻게 표시 할 수 있습니까? 당신은 할 수 없습니다. 그래서 쿼리는 세 가지 모두를 보여줄 것입니다.

또한 "3", 최고 및 2 카운트의 "2"가있는 경우 두 번째 순위가 같으므로 상위 2 개를 표시 할 수 없으므로 검색어에 모두 세.

집계 결과 집합으로 임시 테이블을 만든 다음 쿼리를 실행하면 쿼리가 더 간단 해집니다.

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

오후 모두, 매우 감사합니다. 매우 높이 평가 된 의견입니다. 존 감사합니다 – jono2010

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

expr1을는 CONCAT (...) 등일 수있다. ";"를 숨기려면 REPLACE를 사용하십시오.