2009-10-27 16 views
5

semplificata Struttura della tabella:Mysql GROUP BY e contano più le clausole 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`) 
) 

Per questo, ho la seguente query SQL che ammonta semplicemente tutti i record con lo stato definito.

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 

Questa interrogazione è stata semplificata per rimuovere l'inner join ai dati non correlati e le colonne in più che non dovrebbe pregiudicare la questione.

MAX (giorno_attivo) è uguale per tutti i record di un particolare giorno e deve sempre selezionare il giorno più recente oppure consentire un offset da ORA(). (Si tratta di un campo unixtime)

voglio sia il conteggio di: (!! Status = stato 'OK' E = 'riparare')

e l'inverso ... conteggio di: (status = 'OK 'o status = 'riparare')

e la prima risposta diviso per il secondo, per 'percentage_dead'(probabilmente altrettanto veloce per fare in post processing)

della più recente giorno o un offset (- 86400 per ieri, ecc.)

La tabella contiene circa 500k di record e cresce di circa 5000 al giorno quindi una singola query SQL anziché il looping sarebbe davvero bella ..

Immagino che qualche IF creativo possa fare questo. La tua competenza è apprezzata.

EDIT: Sono aperto all'utilizzo di una query SQL diversa per i dati di oggi o per i dati di un offset.

MODIFICA: la query funziona, è abbastanza veloce, ma al momento non posso permettere agli utenti di ordinare sulla colonna percentuale (quella derivante da conteggi positivi e negativi). Questo non è uno stopper, ma permetto loro di ordinare tutto il resto. ORDER BY di questo:

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 

Mi dà: # 1247 - Riferimento 'bad_host_count' non supportato (riferimento alla funzione di gruppo)

EDIT: risolto per una diversa sezione. I seguenti lavori e mi permette di 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 

risposta

3

Se ho ben capito, si desidera ottenere un conteggio dello stato di OK vs nomi host non OK, alla data dell'ultima attività. Destra? E poi dovrebbe essere raggruppato per core.

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; 

Questa è una variante del problema "grande-n-per-gruppo" che vedo un sacco di domande su SQL StackOverflow.

Prima di tutto selezionare solo le righe con la data di attività più recente per nome host, operazione che è possibile eseguire eseguendo un join esterno per le righe con lo stesso nome host e una data_attivo maggiore. Dove non troviamo tale corrispondenza, abbiamo già le ultime righe per ogni nome host dato.

Quindi raggruppare per core e contare le righe per stato.

Questa è la soluzione per la data odierna (presupponendo che nessuna riga abbia un giorno_attivo in futuro).Per limitare il risultato alle righe N giorni fa, devi limitare entrambe le tabelle.

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; 

Per quanto riguarda il rapporto tra nomi di host OK e rotti, io consiglierei solo calcolando che nel codice PHP. SQL non ti consente di fare riferimento a alias di colonne in altre espressioni di elenchi di selezione, quindi dovresti racchiuderlo come sottoquery e in questo caso è più complesso di quanto valga.


Ho dimenticato che hai detto che stai usando un timestamp UNIX. Fai qualcosa del genere:

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

Grazie Bill! Non posso testarlo subito, come ho fatto per il giorno. La prima parte che ottengo. Dovrò studiare il secondo per un po 'penso. :) –

+0

In realtà è un tempo di memorizzazione storico int, non DATETIME. Fare la differenza? –

+0

Ok, cambia il modo in cui si calcola l'offset, ma non la logica generale. Aggiungerò un esempio. –