2009-10-27 5 views
5

structure simplifiée Tableau:Mysql GROUP BY et COUNT pour plusieurs clauses 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`) 
) 

Pour cela, je la requête SQL suivante qui totalise simplement tous les enregistrements avec le statut défini.

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 

Cette requête a été simplifiée pour supprimer le INNER REJOINT à des données non liées et des colonnes supplémentaires qui ne devraient pas avoir une incidence sur la question. MAX (active_date) est le même pour tous les enregistrements d'un jour particulier, et doit toujours sélectionner le jour le plus récent ou autoriser un décalage de NOW() à 0 (0). (C'est un champ unixtime)

Je veux à la fois le nombre de: (! Status = 'OK' et status = 'Réparé')

et l'inverse ... le nombre de: (status = « OK OR status = « réparé »)

et la première réponse divisée par la seconde, pour « percentage_dead » (probablement tout aussi rapide à faire en post-traitement)

pour le dernier jour ou un décalage (- 86400 pour hier, etc ..)

Table contient environ 500k enregistrements et se développe par environ 5 000 par jour, donc une seule requête SQL par opposition à la boucle serait vraiment bien ..

J'imagine que certains IF créatifs pourraient le faire. Votre expertise est appréciée.

EDIT: Je suis ouvert à l'utilisation d'une requête SQL différente pour les données d'aujourd'hui ou les données d'un décalage.

EDIT: La requête fonctionne, elle est assez rapide, mais je ne peux actuellement pas laisser les utilisateurs trier sur la colonne de pourcentage (celle dérivée de mauvais et bons comptes). Ce n'est pas un bouchon de spectacle, mais je leur permet de trier tout le reste. ORDER BY de ceci:

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 

Ça me donne: # 1247 - Référence 'bad_host_count' non pris en charge (référence à la fonction de groupe)

EDIT: Résolu pour une autre section. Les travaux suivants et me permet de 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 

Répondre

3

Si je comprends bien, vous voulez obtenir un compte de l'état OK OK vs hostnames pas, à la date de la dernière activité. Droite? Et puis cela devrait être groupé par noyau.

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; 

Ceci est une variante du problème « le plus grand-n-par-groupe » que je vois beaucoup dans les questions SQL sur StackOverflow. D'abord, vous voulez choisir uniquement les lignes qui ont la dernière date d'activité par nom d'hôte, ce que nous pouvons faire en faisant une jointure externe pour les lignes avec le même nom d'hôte et une plus grande valeur active_date. Là où nous ne trouvons pas une telle correspondance, nous avons déjà les dernières lignes pour chaque nom d'hôte donné.

Puis regroupez par cœur et comptez les rangées par état.

C'est la solution pour la date d'aujourd'hui (en supposant qu'aucune ligne n'a de date active_date dans le futur).Pour restreindre le résultat aux lignes il y a N jours, vous devez restreindre les deux tables.

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 ce qui concerne le rapport entre hostnames OK et cassé, je recommande simplement calculer que dans votre code PHP. SQL ne vous permet pas de référencer les alias de colonne dans d'autres expressions de liste de sélection, donc vous devrez envelopper ce qui précède en tant que sous-requête et c'est plus complexe que cela ne vaut dans ce cas.


J'ai oublié que vous avez indiqué que vous utilisez un horodatage UNIX. Faites quelque chose comme ceci:

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

Merci Bill! Je ne peux pas tester cela immédiatement mais comme je l'ai fait pour la journée. Première partie que je reçois. Je vais devoir étudier la seconde pendant un moment je pense. :) –

+0

Il s'agit en fait d'une époque de stockage int, pas DATETIME. Faites une différence? –

+0

Ok, ça change comment vous calculez le décalage, mais pas la logique générale. Je vais ajouter un exemple. –