2009-10-27 6 views
5

단순화 된 테이블 구조 :이를 위해MySQL의 GROUP BY 및 다중에 대한 COUNT WHERE 절

CREATE TABLE IF NOT EXISTS `hpa` (
    `id` bigint(15) NOT NULL auto_increment, 
    `core` varchar(50) NOT NULL, 
    `hostname` varchar(50) NOT NULL, 
    `status` varchar(255) NOT NULL, 
    `entered_date` int(11) NOT NULL, 
    `active_date` int(11) NOT NULL, 
    PRIMARY KEY (`id`), 
    KEY `hostname` (`hostname`), 
    KEY `status` (`status`), 
    KEY `entered_date` (`entered_date`), 
    KEY `core` (`core`), 
    KEY `active_date` (`active_date`) 
) 

, 나는 간단하게 정의 된 상태로 모든 레코드를 합계 다음과 같은 SQL 쿼리가 있습니다.

SELECT core,COUNT(hostname) AS hostname_count, MAX(active_date) AS last_active 
      FROM `hpa` 
      WHERE 
      status != 'OK' AND status != 'Repaired' 
      GROUP BY core 
      ORDER BY core 

이 쿼리는 INNER 질문에 영향을 미치지 않습니다 관련이없는 데이터와 여분의 열을 조인을 제거하기 위해 간소화되었습니다.

MAX (active_date)는 특정 요일의 모든 레코드에서 동일하며 항상 가장 최근 날짜를 선택하거나 NOW()에서 오프셋을 허용해야합니다. (그것의 UNIXTIME 필드)

내가 원하는

모두의 수 : (! 상태 = 'OK'및 상태 = '수리')의

및 역 ... 수 : (상태 = 'OK 'OR 상태 ='수리 ')

과 두 번째로 나눈 첫 번째 대답에 대한'사후 처리에서 할 아마 그냥 빨리 percentage_dead '()

가장 최근의 일을위한

또는 오프셋 (- 어제 등 86400)

테이블에는 약 500k 개의 레코드가 포함되어 있으며 하루에 약 5000 일이 걸리므로 루핑과 반대되는 단일 SQL 쿼리가 정말 좋을 것입니다 ..

일부 IF가 이것을 할 수 있다고 상상해보십시오. 당신은 전문성을 인정받습니다.

편집 : 오늘의 데이터 또는 오프셋 데이터에서 다른 SQL 쿼리를 사용할 수 있습니다.

EDIT : 쿼리가 작동하지만 충분히 빠르지 만 사용자가 백분율 열 (나쁜 수와 양호한 수에서 파생 된 수)에서 정렬하도록 할 수 없습니다. 이것은 쇼 스토퍼가 아니지만 다른 모든 것을 분류 할 수 있도록 허용합니다. 이것에 의해 ORDER :

SELECT h1.core, MAX(h1.entered_date) AS last_active, 
SUM(CASE WHEN h1.status IN ('OK', 'Repaired') THEN 1 ELSE 0 END) AS good_host_count, 
SUM(CASE WHEN h1.status IN ('OK', 'Repaired') THEN 0 ELSE 1 END) AS bad_host_count 
FROM `hpa` h1 
LEFT OUTER JOIN `hpa` h2 ON (h1.hostname = h2.hostname AND h1.active_date < h2.active_date) 
WHERE h2.hostname IS NULL 
GROUP BY h1.core 
ORDER BY (bad_host_count/(bad_host_count + good_host_count)) DESC,h1.core 

저를 제공합니다 : # 1247 - 참고 'bad_host_count'지원되지 않습니다 (그룹 기능을 참조)

편집 : 다른 섹션에 대한 해결. 다음 작품과 내가 이해한다면 나를 ORDER BY percentage_dead

SELECT c.core, c.last_active, 
SUM(CASE WHEN d.dead = 1 THEN 0 ELSE 1 END) AS good_host_count, 
SUM(CASE WHEN d.dead = 1 THEN 1 ELSE 0 END) AS bad_host_count, 
(SUM(CASE WHEN d.dead = 1 THEN 1 ELSE 0 END) * 100/ 
((SUM(CASE WHEN d.dead = 1 THEN 0 ELSE 1 END))+(SUM(CASE WHEN d.dead = 1 THEN 1 ELSE 0 END)))) AS percentage_dead 
FROM `agent_cores` c 
LEFT JOIN `dead_agents` d ON c.core = d.core 
WHERE d.active = 1 
GROUP BY c.core 
ORDER BY percentage_dead 

답변

3

할 수 있습니다, 당신은 마지막 활동 날짜에하지 OK 호스트 대 OK의 상태의 개수를 얻을 수있다. 권리? 그리고 그 핵심에 의해 그룹화되어야합니다.

SELECT core, MAX(active_date) 
    SUM(CASE WHEN status IN ('OK', 'Repaired') THEN 1 ELSE 0 END) AS OK_host_count, 
    SUM(CASE WHEN status IN ('OK', 'Repaired') THEN 0 ELSE 1 END) AS broken_host_count 
FROM `hpa` h1 LEFT OUTER JOIN `hpa` h2 
    ON (h1.hostname = h2.hostname AND h1.active_date < h2.active_date) 
WHERE h2.hostname IS NULL 
GROUP BY core 
ORDER BY core; 

는 I에 유래에 SQL 질문에서 많이 볼 수있는 "가장 큰-N 당 그룹"문제의 변형입니다.

먼저 호스트 이름 당 최근 활동 날짜가있는 행만 선택하려고합니다. 호스트 이름이 같고 active_date가 큰 행에 대해 외부 조인을 수행하면됩니다. 일치하는 항목이 발견되지 않으면 각 주어진 호스트 이름에 대해 최신 행이 이미 있습니다.

그런 다음 코어별로 그룹화하고 상태에 따라 행을 계산하십시오.

오늘 날짜의 해결책입니다 (앞으로는 행에 active_date가 없다고 가정).N 일 전에 결과를 행으로 제한하려면 두 테이블을 모두 제한해야합니다. 확인 및 깨진 호스트 이름 사이의 비율에 대해서는

SELECT core, MAX(active_date) 
    SUM(CASE WHEN status IN ('OK', 'Repaired') THEN 1 ELSE 0 END) AS OK_host_count, 
    SUM(CASE WHEN status IN ('OK', 'Repaired') THEN 0 ELSE 1 END) AS broken_host_count 
FROM `hpa` h1 LEFT OUTER JOIN `hpa` h2 
    ON (h1.hostname = h2.hostname AND h1.active_date < h2.active_date 
    AND h2.active_date <= CURDATE() - INTERVAL 1 DAY) 
WHERE h1.active_date <= CURDATE() - INTERVAL 1 DAY AND h2.hostname IS NULL 
GROUP BY core 
ORDER BY core; 

, 난 그냥 당신의 PHP 코드에서 그 계산 권하고 싶습니다. SQL에서는 다른 선택 목록 표현식에서 열 별칭을 참조 할 수 없으므로 위의 내용을 하위 쿼리로 묶어야합니다.이 경우에는 그보다 복잡합니다.


UNIX 타임 스탬프를 사용한다는 사실을 잊어 버렸습니다. 다음과 같이하십시오.

SELECT core, MAX(active_date) 
    SUM(CASE WHEN status IN ('OK', 'Repaired') THEN 1 ELSE 0 END) AS OK_host_count, 
    SUM(CASE WHEN status IN ('OK', 'Repaired') THEN 0 ELSE 1 END) AS broken_host_count 
FROM `hpa` h1 LEFT OUTER JOIN `hpa` h2 
    ON (h1.hostname = h2.hostname AND h1.active_date < h2.active_date 
    AND h2.active_date <= UNIX_TIMESTAMP() - 86400) 
WHERE h1.active_date <= UNIX_TIMESTAMP() - 86400 AND h2.hostname IS NULL 
GROUP BY core 
ORDER BY core; 
+0

감사합니다. Bill! 내가 오늘 그 일을 끝내었지만 즉시 이것을 시험 할 수는 없다. 나는 첫 번째 부분을 얻는다. 제 생각에 잠시 동안 공부해야합니다. :) –

+0

사실 그것은 DATETIME이 아니라 에포크 시간을 저장하는 int입니다. 차이를 만들다? –

+0

좋아, 오프셋을 계산하는 방법을 변경하지만 일반 논리는 변경하지 않습니다. 예제를 추가하겠습니다. –