2009-10-27 19 views
5

simplificado la estructura de la tabla:Mysql GROUP BY y contar para múltiples cláusulas 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`) 
) 

Para ello, tengo la siguiente consulta SQL que simplemente se suma el total de 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 el combinaciones internas de datos no relacionados y columnas adicionales que no debería afectar a 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(). (Que es un campo unixtime)

quiero tanto la cuenta de: (! = Estado de estado de 'OK' y = 'reparado')

Y a la inversa ... recuento de: (estado = 'OK 'o estado = 'reparado')

Y la primera respuesta dividido por el segundo, por 'percentage_dead'(probablemente igual de rápido que hacer en el procesamiento posterior)

para bajar el último día o un desplazamiento (- 86400 para ayer, etc.)

La tabla contiene aproximadamente 500k registros y crece por alrededor de 5000 por día, por lo que una única consulta SQL en lugar de bucle sería muy agradable ...

Imagino que algunas IF creativas podrían hacer esto. Tu experiencia es apreciada.

EDITAR: Estoy abierto a utilizar 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 dejar que los usuarios ordenen en la columna de porcentaje (la derivada de los recuentos malos y buenos). Esto no es un show stopper, pero les permito clasificar todo lo demás. El ORDER BY 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 - Referencia 'bad_host_count' no es compatible (referencia a la función de grupo)

EDIT: Resuelto por una sección diferente. Las siguientes obras y 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 

Respuesta

3

Si entiendo, que quieren obtener un recuento del estado de OK vs nombres de host no está bien, en la fecha de la última actividad. ¿Derecha? Y luego eso debe ser agrupado 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 del problema "grande-n-por-grupo" que veo mucho en cuestiones 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 combinación externa para las filas con el mismo nombre de host y una fecha activa más grande. 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 active_date en el futuro).Para restringir el resultado a las filas hace N días, 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; 

En cuanto a la relación entre los nombres de host OK y rotas, lo recomiendo simplemente calculando que 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; 
+0

Gracias Bill! No puedo probar esto inmediatamente aunque ya terminé el día. Primera parte que obtengo Tendré que estudiar el segundo por un tiempo, creo. :) –

+0

En realidad, es un tiempo de almacenamiento int, no DATETIME. ¿Hacer la diferencia? –

+0

Bien, cambia la forma de calcular el desplazamiento, pero no la lógica general. Añadiré un ejemplo. –