2009-10-27 15 views
5

Uproszczona struktura Tabela:MySQL GROUP BY i liczyć na wielokrotność 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`) 
) 

W tym mam następujące zapytanie SQL, które po prostu wynosi obecnie wszystkie rekordy z określonym statusie.

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 

Ta kwerenda została uproszczona w celu usunięcia sprzężenia wewnętrzne niepowiązanych danych i dodatkowych kolumn, które nie powinny mieć wpływu na to pytanie.

MAX (data_aktywna) jest taka sama dla wszystkich rekordów danego dnia i zawsze powinien wybrać ostatni dzień lub zezwolić na przesunięcie od TERAZ(). (Jest to pole unixtime)

Chcę zarówno Ilość: (! = Stan status 'OK' i = 'Naprawiony')

I odwrotna ... Ilość: (status = „OK 'OR status = «Naprawiony»)

i pierwsza odpowiedź podzielona przez sekundę, dla «percentage_dead» (Prawdopodobnie tak szybko zrobić w postprocessing)

za ostatni dzień lub offset (- 86400 za wczoraj itd.)

Tabela zawiera około 500 tys. Rekordów i rośnie o około 5000 dziennie, więc pojedyncze zapytanie SQL w przeciwieństwie do zapętlania byłoby naprawdę miłe ..

Wyobrażam sobie, że twórczy IF może to zrobić. Twoja wiedza jest doceniana.

EDYCJA: Jestem otwarty na użycie innego zapytania SQL dla danych bieżących lub danych z offsetu.

EDYCJA: Zapytanie działa, jest wystarczająco szybkie, ale obecnie nie mogę pozwolić użytkownikom sortować kolumny procentowej (tej, która pochodzi ze złych i dobrych wyników). To nie jest korek na pokaz, ale pozwalam im sortować wszystko. ORDER BY tego:

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 

daje mi: # 1247 - Reference 'bad_host_count' nie są obsługiwane (odniesienie do grupy funkcyjnej)

EDIT: Zrobione dla innej sekcji. Następujące prace i pozwala mi 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 

Odpowiedz

3

Jeśli dobrze rozumiem, chcesz uzyskać liczbę statusu OK vs. nie ok hostów, w dniu ostatniej aktywności. Dobrze? A to powinno być pogrupowane według rdzenia.

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; 

Jest to odmiana "największego na n grup" problemu, który widzę dużo w pytaniach SQL na temat StackOverflow.

Najpierw wybierz tylko te wiersze, które mają datę ostatniej aktywności na nazwę hosta, co możemy zrobić, wykonując sprzężenie zewnętrzne dla wierszy o tej samej nazwie hosta i większej wartości active_date. Tam, gdzie nie znajdziemy takiego dopasowania, mamy już ostatnie wiersze dla każdej podanej nazwy hosta.

Następnie grupuj według rdzenia i policz wiersze według stanu.

To jest rozwiązanie dla dzisiejszej daty (zakładając, że żaden wiersz nie ma daty_aktywnej w przyszłości).Aby ograniczyć wyniki do wierszy N dni temu, musisz ograniczyć obie tabele.

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; 

Jeśli chodzi o stosunek między OK i połamanych hostów, polecam tylko obliczenia, które w kodzie PHP. SQL nie zezwala na odwoływanie się do aliasów kolumn w innych wyrażeniach listy wyboru, więc trzeba zawijać powyższe dane jako podzapytanie i jest to bardziej złożone niż w tym przypadku warte.


Zapomniałem, że powiedziałeś, że używasz znacznika czasu UNIX. Zrób coś takiego:

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

Dziękuję Bill! Nie mogę tego przetestować natychmiast, tak jak skończyłem na cały dzień. Pierwsza część dostaję. Przez chwilę będę musiał zastanowić się nad sekundą. :) –

+0

To właściwie int przechowujący czas epoki, a nie DATETIME. Robić różnicę? –

+0

OK, zmienia sposób obliczania przesunięcia, ale nie ogólnej logiki. Dodam przykład. –