2009-06-04 12 views
1

J'ai une table dans SQL qui a quatre colonnes. Deux d'entre elles sont des colonnes de propriétés et de valeurs qui stockent des mesures de performance pour l'un de nos serveurs. Nous sommes constamment à la recherche de nouvelles mesures de performance et nous ne voulions pas continuer à repenser notre schéma, c'est pourquoi nous avons conçu la table de cette façon. Problème, quand je crée une vue pour regarder la table comme si elle est correctement normalisée, je reçois une requête qui crie juste "Oh mon dieu c'est un code de merde" puisqu'elle implique une table jointe à elle-même 12 fois. Voici la requête que j'ai utilisée pour la vue. Fondamentalement, il se sent comme si je fais quelque chose de vraiment mal, mais je ne peux pas trouver une meilleure façon de résoudre le problème.Comment créer une vue d'une table de valeurs de propriété dans SQL

SELECT 
    astats.AQTORStatsID, 
    astats.ServerName, 
    astats.Remarks, 
    astats.StatsBeginDateTime, 
    astats.StatsEndDateTime, 
    asi1.AQTORStatValue as 'QtCPU_Average', 
    asi2.AQTORStatValue as 'QtCPU_TopQuintile', 
    asi3.AQTORStatValue as 'QtCPU_TopOnePercent', 
    asi4.AQTORStatValue as 'QtCl_Average', 
    asi5.AQTORStatValue as 'QtCl_TopQuintile', 
    asi6.AQTORStatValue as 'QtCl_TopOnePercent', 
    asi7.AQTORStatValue as 'UpdPrcStd_Average', 
    asi8.AQTORStatValue as 'UpdPrcStd_TopQuintile', 
    asi9.AQTORStatValue as 'UpdPrcStd_TopOnePercent', 
    asi10.AQTORStatValue as 'RcRsUPr_Average', 
    asi11.AQTORStatValue as 'RcRsUPr_TopQuintile', 
    asi12.AQTORStatValue as 'RcRsUPr_TopOnePercent' 
FROM 
    tb_rAQTORStatsItem asi1 
    INNER JOIN tb_rAQTORStatsItem asi2 ON asi1.AQTORStatsID = asi2.AQTORStatsID 
    INNER JOIN tb_rAQTORStatsItem asi3 ON asi2.AQTORStatsID = asi3.AQTORStatsID 
    INNER JOIN tb_rAQTORStatsItem asi4 ON asi3.AQTORStatsID = asi4.AQTORStatsID 
    INNER JOIN tb_rAQTORStatsItem asi5 ON asi4.AQTORStatsID = asi5.AQTORStatsID 
    INNER JOIN tb_rAQTORStatsItem asi6 ON asi5.AQTORStatsID = asi6.AQTORStatsID 
    INNER JOIN tb_rAQTORStatsItem asi7 ON asi6.AQTORStatsID = asi7.AQTORStatsID 
    INNER JOIN tb_rAQTORStatsItem asi8 ON asi7.AQTORStatsID = asi8.AQTORStatsID 
    INNER JOIN tb_rAQTORStatsItem asi9 ON asi8.AQTORStatsID = asi9.AQTORStatsID 
    INNER JOIN tb_rAQTORStatsItem asi10 ON asi9.AQTORStatsID = asi10.AQTORStatsID 
    INNER JOIN tb_rAQTORStatsItem asi11 ON asi10.AQTORStatsID = asi11.AQTORStatsID 
    INNER JOIN tb_rAQTORStatsItem asi12 ON asi11.AQTORStatsID = asi12.AQTORStatsID 
    INNER JOIN tb_dAQTORStats astats on asi12.AQTORStatsID = astats.AQTORStatsID 
WHERE 
    asi1.AQTORStatName = 'QtCPU_Average' 
AND asi2.AQTORStatName = 'QtCPU_TopQuintile' 
AND asi3.AQTORStatName = 'QtCPU_TopOnePercent' 
AND asi4.AQTORStatName = 'QtCl_Average' 
AND asi5.AQTORStatName = 'QtCl_TopQuintile' 
AND asi6.AQTORStatName = 'QtCl_TopOnePercent' 
AND asi7.AQTORStatName = 'UpdPrcStd_Average' 
AND asi8.AQTORStatName = 'UpdPrcStd_TopQuintile' 
AND asi9.AQTORStatName = 'UpdPrcStd_TopOnePercent' 
AND asi10.AQTORStatName = 'RcRsUPr_Average' 
AND asi11.AQTORStatName = 'RcRsUPr_TopQuintile' 
AND asi12.AQTORStatName = 'RcRsUPr_TopOnePercent' 

Répondre

2

Voici comment faire:

SELECT 
    astats.AQTORStatsID 
, astats.ServerName 
, astats.Remarks 
, astats.StatsBeginDateTime 
, astats.StatsEndDateTime 

, QtCPU_Average   = max(case when asi.AQTORStatName = 'QtCPU_Average'   then asi.AQTORStatValue end) 
, QtCPU_TopQuintile  = max(case when asi.AQTORStatName = 'QtCPU_TopQuintile'  then asi.AQTORStatValue end) 
, QtCPU_TopOnePercent  = max(case when asi.AQTORStatName = 'QtCPU_TopOnePercent'  then asi.AQTORStatValue end) 
, QtCl_Average   = max(case when asi.AQTORStatName = 'QtCl_Average'   then asi.AQTORStatValue end) 
, QtCl_TopQuintile  = max(case when asi.AQTORStatName = 'QtCl_TopQuintile'  then asi.AQTORStatValue end) 
, QtCl_TopOnePercent  = max(case when asi.AQTORStatName = 'QtCl_TopOnePercent'  then asi.AQTORStatValue end) 
, UpdPrcStd_Average  = max(case when asi.AQTORStatName = 'UpdPrcStd_Average'  then asi.AQTORStatValue end) 
, UpdPrcStd_TopQuintile = max(case when asi.AQTORStatName = 'UpdPrcStd_TopQuintile' then asi.AQTORStatValue end) 
, UpdPrcStd_TopOnePercent = max(case when asi.AQTORStatName = 'UpdPrcStd_TopOnePercent' then asi.AQTORStatValue end) 
, RcRsUPr_Average   = max(case when asi.AQTORStatName = 'RcRsUPr_Average'   then asi.AQTORStatValue end) 
, RcRsUPr_TopQuintile  = max(case when asi.AQTORStatName = 'RcRsUPr_TopQuintile'  then asi.AQTORStatValue end) 
, RcRsUPr_TopOnePercent = max(case when asi.AQTORStatName = 'RcRsUPr_TopOnePercent' then asi.AQTORStatValue end) 

from tb_dAQTORStats astats 
join tb_rAQTORStatsItem asi on asi.AQTORStatsID = astats.AQTORStatsID 

group by 
    astats.AQTORStatsID 
, astats.ServerName 
, astats.Remarks 
, astats.StatsBeginDateTime 
, astats.StatsEndDateTime 

Notes:

  1. Un bon éditeur de texte avec le mode colonne ou édition rectangulaire aide vraiment avec ce genre de chose. UltraEdit ou Emacs viennent à l'esprit. J'ai créé le dessus dans UltraEdit dans environ une minute.

  2. Votre requête initiale avec INNER JOINs aurait supprimé toutes les lectures pour un serveur donné si un type de lecture manquait. Pas bon. Cette requête retournera une ligne par serveur pour tout serveur avec au moins une lecture. Si vous souhaitez renvoyer tous les serveurs, qu'il y ait ou non des lectures, modifiez INNER JOIN à LEFT JOIN.

  3. Vous n'avez pas besoin d'une table séparée pour vos noms de statistiques, sauf si vous devez appliquer l'intégrité des données via une clé étrangère.

+0

Nous utilisons la table parent pour stocker des informations sur ce que nous faisions pendant la période pour laquelle les statistiques ont été générées. C'est-à-dire, il a des remarques pour quelles techniques améliorant la performance que nous utilisions et il a d'autres informations qui se rapportent à toutes les statistiques recueillies. –

+0

J'aime cette solution car je n'ai pas le nombre fou de jointures. Merci pour l'idée. –

3

à mon humble avis, ce genre de code (ainsi que les indices de la conception de la panne) fait partie du prix que vous payez pour la flexibilité de l'idiome « ​​table de propriété » - vous paie votre argent, et vous prend votre choix ! -)

0

ont une table contenant tous les noms possibles des stats (que je pense que vous devriez avoir de toute façon, avec une contrainte FKEY de tb_rAQTORStatsItem sur elle)

Ensuite, vous pouvez avoir quelque chose comme:

SELECT astats.QTORStatsID, astats.ServerName, astats.Remarks, 
     astats.StatsBeginDateTime, astats.StatsEndDateTime, 
     max(case item.AQTORStatName when 'QtCPU_Average' then AQTORStatValue end) as QtCPU_Average, 
     max(case item.AQTORStatName when 'QtCPU_TopQuintile' then AQTORStatValue end) as QtCPU_TopQuintile, 
     /* repeat for each statistic... */ 
FROM tb_dAQTORStats astats 
    CROSS JOIN tb_rAQTORStatNames statnames 
    LEFT JOIN tb_rAQTORStatsItem item 
      ON item.AQTORStatName = statnames.AQTORStatName 
      AND item.AQTORStatsID = astats.AQTORStatsID 
GROUP BY astats.QTORStatsID, astats.ServerName, astats.Remarks, 
     astats.StatsBeginDateTime, astats.StatsEndDateTime 

Bien que ce soit encore assez moche, au moins vous pouvez couper la jointure N-way à une jointure à 3 voies. Oui, c'est un cas de flexibilité et la simplicité commence en désaccord. L'ajout de colonnes est en quelque sorte un moyen plus simple de résoudre le problème, car le schéma de la base de données correspond exactement aux statistiques que vous collectez et de quel type elles sont: mais vous devez les synchroniser.

(syntaxe nb de requête n'a pas été testé, je suppose CROSS JOIN fonctionne de la manière que je pense)

0

Ce type de conception se situe à peu près à mi-chemin de la version relationnelle à la version pure EAV (Entity-Attribute-Value). Et oui, ils ont des aspects hideux pour une base de données SQL La meilleure solution à ce problème que j'ai trouvé est d'utiliser des cubes et/ou des tableaux croisés dynamiques, au lieu d'essayer de marteler cette cheville ronde dans le trou carré des vues relationnelles. Les choses que vous pouvez utiliser sont bien adaptées: SSAS, rapports pivot de Reporting Services, et même les tableaux croisés dynamiques Excel.

Questions connexes