Pregunta

Estructura de tabla 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`)
)

Para esto, tengo la siguiente consulta SQL que simplemente suma todos los registros con el 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 se ha simplificado para eliminar las uniones internas a datos no relacionados y columnas adicionales que no deberían afectar la pregunta.

MAX (active_date) es el mismo para todos los registros de un día en particular, y siempre debe seleccionar el día más reciente o permitir un desplazamiento desde NOW (). (es un campo UNIXTIME)

Quiero el recuento de: (estado! = 'OK' Y estado! = 'Reparado')

Y el inverso ... recuento de: (estado = 'OK' O estado = 'Reparado')

Y la primera respuesta dividida por la segunda, para 'percent_dead' (probablemente igual de rápido en el procesamiento posterior)

PARA el día más reciente o un desplazamiento (- 86400 para ayer, etc.)

La tabla contiene aproximadamente 500k registros y crece aproximadamente 5000 al día, por lo que una sola consulta SQL en lugar de bucle sería realmente agradable ...

Me imagino que algunos IF creativos podrían hacer esto. Su experiencia es apreciada.

EDITAR: estoy abierto a usar una consulta SQL diferente para los datos de hoy o los datos de un desplazamiento.

EDITAR: La consulta funciona, es lo suficientemente rápida, pero actualmente no puedo permitir que los usuarios clasifiquen en la columna de porcentaje (la derivada de conteos malos y buenos). Este no es un show stopper, pero les permito ordenar todo lo demás. El ORDEN DE esto:

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

Me da:     # 1247 - No se admite la referencia 'bad_host_count' (referencia a la función de grupo)

EDITAR: Resuelto para una sección diferente. Lo siguiente funciona y me permite ORDER BY porcentaje_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
¿Fue útil?

Solución

Si entiendo, desea obtener un recuento del estado de los nombres de host OK vs. not OK, en la fecha de la última actividad. ¿Correcto? Y luego eso debería agruparse 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 es una variación de la "mayor-n-por-grupo" problema que veo mucho en las preguntas de SQL en StackOverflow.

Primero queremos elegir solo las filas que tienen la última fecha de actividad por nombre de host, lo que podemos hacer haciendo una unión externa para las filas con el mismo nombre de host y una mayor fecha_activo. Donde no encontramos tal coincidencia, ya tenemos las últimas filas para cada nombre de host dado.

Luego, agrupe por núcleo y cuente las filas por estado.

Esa es la solución para la fecha de hoy (suponiendo que ninguna fila tenga una fecha_activo en el futuro). Para restringir el resultado a filas N días atrás, debe restringir ambas tablas.

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; 

Con respecto a la relación entre OK y nombres de host rotos, recomendaría simplemente calcular eso en su código PHP. SQL no le permite hacer referencia a alias de columna en otras expresiones de lista de selección, por lo que tendría que ajustar lo anterior como una subconsulta y eso es más complejo de lo que vale en este caso.


Olvidé que dijiste que estás usando una marca de tiempo UNIX. Haga algo como esto:

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 bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top