web-dev-qa-db-fra.com

Vous souhaitez comparer deux sous-ensembles de données d'une même table?

Mon problème:

J'essaie de comparer des sous-ensembles de données dans une table, et j'ai deux méthodes qui fonctionnent partiellement et une certitude qu'il doit y avoir une façon plus correcte de le faire.

L'idée ici est un tableau qui contient des ensembles de données sur les mêmes systèmes au fil du temps, et je voudrais les comparer et surtout voir quand il y a des introductions ou des absences. Permettez-moi de démontrer avec une simple table de test:

mysql> select * from gocore;
+-----+------------+------+----------+----------+
| uid | server     | tag  | software | revision |
+-----+------------+------+----------+----------+
|   1 | enterprise | old  | Apache   | 2.2.25   |
|   2 | enterprise | new  | Apache   | 2.4.6    |
|   3 | enterprise | new  | Tomcat   | 7.0.42   |
|   4 | enterprise | old  | geronimo | 2.1.7    |
+-----+------------+------+----------+----------+

Dans cet exemple, il existe deux jeux de données: le jeu de données "ancien" et le jeu de données "nouveau". Chacun reflète un échantillon de données prélevé à un moment donné. Pour le serveur "entreprise", nous avons un progiciel qui a changé au fil du temps (Apache), un progiciel qui a été introduit (Tomcat) et un progiciel qui est devenu absent (geronimo).

Mon objectif: Une requête qui me permettra de résumer l'état entre "ancien" et "nouveau":

+------------+----------+----------+----------+
| server     | software | revision | revision |
+------------+----------+----------+----------+
| enterprise | Apache   | 2.2.25   | 2.4.6    |
| enterprise | geronimo | 2.1.7    | NULL     |
| enterprise | Tomcat   | NULL     | 7.0.42   |
+------------+----------+----------+----------+

Il est important pour mes besoins de pouvoir voir les cellules "NULL" ci-dessus - j'ai besoin de savoir quand un logiciel a été ajouté ou supprimé du système. POUR ÊTRE CLAIR, le tableau ci-dessus n'est pas le résultat d'une requête - c'est moi qui utilise un éditeur de texte pour corriger ce que j'obtenais pour décrire ce que je cherche. J'ai besoin de votre aide pour comprendre la requête qui créerait ce tableau :)

Mes félicitations:

Si j'effectue un LEFT JOIN et utilise la clause WHERE pour faire la distinction entre les "anciennes" et les "nouvelles" balises, j'obtiens des résultats uniquement pour les entrées qui existent sous les deux balises:

mysql> select old.server, old.software, old.revision, new.software, new.revision
    -> from gocore as old left join gocore as new on old.software = new.software
    -> where old.tag = 'old' and new.tag = 'new';
+------------+----------+----------+----------+----------+
| server     | software | revision | software | revision |
+------------+----------+----------+----------+----------+
| enterprise | Apache   | 2.2.25   | Apache   | 2.4.6    |
+------------+----------+----------+----------+----------+

Mon prochain essai a été de créer deux vues afin de pouvoir effectuer le JOIN sans lancer le filtre de balises dans le mix:

mysql> create view gc_old as select uid,server,tag,software,revision
    -> from gocore where tag = 'old';
Query OK, 0 rows affected (0.00 sec)

mysql> create view gc_new as select uid,server,tag,software,revision
    -> from gocore where tag = 'new';
Query OK, 0 rows affected (0.00 sec)

mysql> select * from gc_old;
+-----+------------+------+----------+----------+
| uid | server     | tag  | software | revision |
+-----+------------+------+----------+----------+
|   1 | enterprise | old  | Apache   | 2.2.25   |
|   4 | enterprise | old  | geronimo | 2.1.7    |
+-----+------------+------+----------+----------+
2 rows in set (0.00 sec)

mysql> select * from gc_new;
+-----+------------+------+----------+----------+
| uid | server     | tag  | software | revision |
+-----+------------+------+----------+----------+
|   2 | enterprise | new  | Apache   | 2.4.6    |
|   3 | enterprise | new  | Tomcat   | 7.0.42   |
+-----+------------+------+----------+----------+
2 rows in set (0.00 sec)

mysql> select old.server, old.software, old.revision, new.revision
    -> from gc_old as old left join gc_new as new
    -> on old.software = new.software;
+------------+----------+----------+----------+
| server     | software | revision | revision |
+------------+----------+----------+----------+
| enterprise | Apache   | 2.2.25   | 2.4.6    |
| enterprise | geronimo | 2.1.7    | NULL     |
+------------+----------+----------+----------+
2 rows in set (0.00 sec)

Cela fonctionne mieux - je vois maintenant l'absence, pas seulement le changement, mais je ne vois toujours pas l'introduction. Et j'ai dû créer des vues pour cela, ce qui me semble aussi inflexible que l'ensemble de données est ajouté au fil du temps.

Mes questions:

  1. Comment puis-je m'assurer que tous les trous apparaissent, comme représenté par les cellules NULL dans la section "Mon objectif" ci-dessus?
  2. Puis-je le faire dans une seule requête, en évitant la création de vues comme une béquille?

Toute aide que vous pouvez fournir est fortement appréciée. Merci!

6
gowenfawr

Je pense que vous devez le pirater un peu avec une table dérivée, AKA une table temporaire implicite, AKA un " sous-requête dans la clause from ."

Nous dérivons une table que nous appellerons "t" contenant chaque distinct (serveur, logiciel) de gocore, puis nous avons joint deux fois gocore, une fois sur tag = 'old' et une fois sur tag = 'new'.

SELECT t.server, t.software, o.revision AS old_rev, n.revision AS new_rev
  FROM (SELECT DISTINCT server, software FROM gocore) t
  LEFT JOIN gocore o ON o.server = t.server AND o.software = t.software AND o.tag = 'old'
  LEFT JOIN gocore n ON n.server = t.server AND n.software = t.software AND n.tag = 'new';
9
Michael - sqlbot