Pergunta

Estrutura da tabela simplificada:

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`)
)

Por isso, eu tenho a seguinte consulta SQL que simplesmente totaliza-se todos os registros com o estado definido.

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

Esta consulta foi simplificado para remover a associações internas aos dados não relacionados e colunas extras que não devem afetar a questão.

MAX (active_date) é a mesma para todos os registros de um determinado dia, e deve sempre escolher o dia mais recente, ou permitir um deslocamento do NOW (). (É um campo unixtime)

Eu quero tanto a contagem de: (! Status = 'OK' e status = 'reparado')

E o inverso ... Contagem de: (status = 'OK' OR status = 'reparado')

E a primeira resposta dividido pelo segundo, por 'percentage_dead' (provavelmente tão rápido que fazer em pós-processamento)

Para o dia mais recente ou um deslocamento (- 86400 para ontem, etc ..)

Table contém cerca de 500 mil registros e cresce cerca de 5000 por dia para uma única consulta SQL ao invés de looping seria muito legal ..

Eu imagino alguns criativo se de poderia fazer isso. Você especialização é apreciado.

EDIT:. Estou aberto a usando uma consulta SQL diferente tanto para os dados de hoje, ou dados de um deslocamento

EDIT: consulta funciona, é rápido o suficiente, mas eu atualmente não podem deixar os usuários de ordenação na coluna percentual (aquele derivado de ruim e boas contagens). Este não é um show rolha, mas eu lhes permitem classificar em tudo o resto. A ORDER BY deste:

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

Dá-me: # 1247 - Reference 'bad_host_count' não suportado (referência a função de grupo)

EDIT: Resolvido para uma seção diferente. As seguintes obras e me permite 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
Foi útil?

Solução

Se eu entendi, você deseja obter uma contagem do status de OK vs. Não OK nomes de host, na data da última atividade. Certo? E, em seguida, que devem ser agrupados por núcleo.

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;

Esta é uma variação do problema "maior-n-per-grupo" que eu vejo um monte de perguntas SQL no StackOverflow.

Primeiro querer escolher apenas as linhas que têm a data atividade mais recente por hostname, o que podemos fazer por fazer uma junção externa para linhas com o mesmo nome de host e um maior active_date. Onde encontramos nenhuma partida, já temos as últimas linhas para cada dada hostname.

grupo Então por núcleo e contar as linhas de status.

Essa é a solução para a data de hoje (supondo que nenhuma linha tem um active_date no futuro). Para restringir os resultados às linhas N dias atrás, você tem que restringir ambas as tabelas.

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; 

No que diz respeito a relação entre OK e nomes de máquinas quebradas, eu recomendo apenas calculando que em seu código PHP. O SQL não permitem apelidos de colunas de referência em outras expressões select-list, então você teria que envolver o acima como uma subconsulta e isso é mais complexo do que vale a pena neste caso.


Esqueci que você disse que você está usando um timestamp UNIX. Fazer algo como isto:

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; 
Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top