Domanda

Struttura della tabella semplificata:

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 somma 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 query è stata semplificata per rimuovere i JOIN INNER in dati non correlati e colonne aggiuntive che non dovrebbero influire sulla domanda.

MAX (active_date) è lo stesso per tutti i record di un determinato giorno e deve sempre selezionare il giorno più recente o consentire un offset da NOW (). (è un campo UNIXTIME)

Voglio sia il conteggio di: (status! = 'OK' AND status! = 'Repaired')

E il conteggio inverso ... di: (status = 'OK' OR status = 'Riparato')

E la prima risposta divisa per la seconda, per "percent_dead" (probabilmente altrettanto veloce da fare in post elaborazione)

PER l'ultimo giorno o un offset (- 86400 per ieri, ecc.)

La tabella contiene circa 500k record e cresce di circa 5000 al giorno, quindi una singola query SQL invece del loop sarebbe davvero piacevole ..

Immagino che alcuni IF creativi potrebbero farlo. La tua esperienza è apprezzata.

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

EDIT: la query funziona, è abbastanza veloce, ma al momento non posso permettere agli utenti di ordinare sulla colonna percentuale (quella derivata da conteggi errati e positivi). Questo non è un fermo spettacolo, ma permetto loro di ordinare tutto il resto. L'ORDINE 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 sezione diversa. Il seguente funziona e mi permette di ORDER BY percent_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
È stato utile?

Soluzione

Se ho capito, vuoi ottenere un conteggio dello stato dei nomi host OK e non OK, alla data dell'ultima attività. Destra? E poi questo 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 variazione del "più grande n per gruppo" problema che vedo molto nelle domande SQL su StackOverflow.

Innanzitutto, desidero scegliere solo le righe con la data di attività più recente per nome host, cosa che possiamo fare eseguendo un join esterno per le righe con lo stesso nome host e una data_attiva maggiore. Laddove non troviamo tale corrispondenza, disponiamo già delle ultime righe per ciascun nome host specificato.

Quindi raggruppa per core e conta le righe per stato.

Questa è la soluzione per la data odierna (supponendo che nessuna riga abbia un active_date 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 OK e nomi host non funzionanti, ti consiglio di calcolarlo nel tuo codice PHP. SQL non ti consente di fare riferimento agli alias di colonna in altre espressioni dell'elenco di selezione, quindi dovresti racchiudere quanto sopra come sottoquery e questo è più complesso di quanto valga in questo caso.


Ho dimenticato che hai detto che stai utilizzando 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; 
Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top