2010-06-22 4 views
3

J'ai une table de paramètres construite avec 'category' déterminant différents produits, 'varname' étant le nom de la variable, et 'info' étant la valeur du paramètre.Comment comparer élégamment les valeurs d'une même table dans TSQL

donc, par exemple,

select top 6 category, varname, info 
from settings_table 
where NODE_NAME='HTT-COMM-A' 
    and section='Module Settings' 
    and category in ('ProductA', 'ProductB') 
order by varname 

résultats dans:

 
category varname    info 
ProductB WEB_ACCESS_ALLOW NO 
ProductA WEB_ACCESS_ALLOW NO 
ProductB WEB_ACCESS_BLOCK YES 
ProductA WEB_ACCESS_BLOCK YES 
ProductB WEB_ACCOUNT_DETAIL NO 
ProductA WEB_ACCOUNT_DETAIL YES 

je voudrais générer une simple liste des différences entre les valeurs lorsque la catégorie = 'ProduitA' et 'ProductB' . Je peux penser à un certain nombre de façons de le faire avec une table temporaire, ou par un certain nombre de sous-requêtes (par exemple, cette douloureuse):

select a.category, a.varname, a.info , b.category, b.info 
from (select category, varname, info, description 
     from settings_table 
     where category = 'ProductA') as a, 
    (select category, varname,info, description 
     from settings_table 
     where category = 'ProductB') as b 
where a.varname=b.varname and a.info != b.info 

mais la méthode ci-dessus (au moins) échoue quand il y a varname dans b qui n'est pas dans a. (Toutes les solutions devraient résoudre ce problème, toutes les différences dans les noms de variable entre a et b devraient également être représentées.)

Ce n'est pas un problème difficile à résoudre d'une manière kludgy, mais je me demande s'il y a une 'bonne façon 'pour le faire avec élégance, sans les sous-sélections horribles ou sans les mises en garde ci-dessus.

Ceci est plus agnostique SQL, mais cette table particulière est dans un serveur MSSQL.

Merci, Rk

+0

Je me sens comme BINARY_CHECKSUM sur les champs en question est le point de départ. Au-delà de ça, j'ai de la difficulté à visualiser comment la jointure pourrait fonctionner, ou comment interroger le nombre de checksum = 1. –

Répondre

2

Si vous ne se souciaient les valeurs de varname et d'information, vous pouvez faire quelque chose comme:

Select varname, info 
From @Data As T 
Except (
     Select varname, info 
     From @Data As T1 
     Where category = 'ProductA' 
     Intersect 
     Select varname, info 
     From @Data As T2 
     Where category = 'ProductB' 
     ) 

Si vous voulez d'autres colonnes de la table source, alors vous pouvez faire quelque chose comme:

Select T.* 
From settings_table As T 
    Left Join (
       Select T1.varname, T1.info 
       From settings_table As T1 
       Where T1.category = 'ProductA' 
        And T1.NODE_NAME='HTT-COMM-A' 
        And T1.section='Module Settings' 
       Intersect 
       Select T2.varname, T2.info 
       From settings_table As T2 
       Where T1.category = 'ProductB' 
        And T1.NODE_NAME='HTT-COMM-A' 
        And T1.section='Module Settings' 
       ) As Z 
     On Z.varname = T.varname 
      And Z.info = T.info 
Where Z.varname Is Null 
    And T.NODE_NAME='HTT-COMM-A' 
    And T.section='Module Settings' 

Pourtant, une troisième voie serait d'utiliser simplement un prédicat EXISTS:

Select T.* 
From settings_table As T 
Where T.NODE_NAME='HTT-COMM-A' 
    And T.section='Module Settings' 
    And Not Exists (
        Select 1 
        From settings_table As T2 
        Where T2.category In('ProductA','ProductB') 
         And T2.varname = T.varname 
         And T2.info = T.info 
        Group By T2.varname, T2.info 
        Having Count(*) = 2 
        ) 
+0

Le prédicat existe est une façon assez élégante de l'approcher ... –

2

Vous pouvez utiliser un autojointure pour y parvenir:

select a.varname as varname, 
a.info as 'ProductA_Setting', 
b.info as 'ProductB_Setting' 
from @t a 
inner join @t b 
on a.varname = b.varname 
where a.category = 'ProductA' 
and b.category = 'ProductB' 
and a.info <> b.info 

Voici le script que j'utilisé pour tester:

declare @t table (category varchar(32), varname varchar(32), info varchar(32)) 

insert into @t select 'ProductB', 'WEB_ACCESS_ALLOW', 'NO' 
insert into @t select 'ProductA', 'WEB_ACCESS_ALLOW', 'NO' 
insert into @t select 'ProductB', 'WEB_ACCESS_BLOCK', 'YES' 
insert into @t select 'ProductA', 'WEB_ACCESS_BLOCK', 'YES' 
insert into @t select 'ProductB', 'WEB_ACCOUNT_DETAIL', 'NO' 
insert into @t select 'ProductA', 'WEB_ACCOUNT_DETAIL', 'YES' 

select * from @t 

select a.varname as varname, 
a.info as 'ProductA_Setting', 
b.info as 'ProductB_Setting' 
from @t a 
inner join @t b 
on a.varname = b.varname 
where a.category = 'ProductA' 
and b.category = 'ProductB' 
and a.info <> b.info 
+0

Cela fonctionnera tant que ProductB n'a pas de paramètres. Ajouter Le suivant, insérer et je suppose que WEB_ACCOUNT_DISABLED ne sera pas listé: insérer dans @t select 'ProductB', 'WEB_ACCOUNT_DISABLED', 'OUI' –

+0

@brian chandley: Vrai. J'étais la solution OP comme base, ce qui excluait également les paramètres non-correspondants. –

+0

pk - J'ai posté le mien comme exemple d'un schéma erroné - comment changeriez-vous le vôtre pour noter si ProductB a un paramètre que ProductA n'a pas? –

1

Vous voulez utiliser un CTE et une jointure externe complète pour cela, je pense:

WITH SETTINGS (category, varname, info) 
AS 
(
    SELECT category, varname, info 
    FROM settings_table 
    WHERE NODE_NAME = 'HTT-COMM-A' 
     AND [section] = 'Module Settings' 
     AND category IN ('ProductA', 'ProductB') 
) 
SELECT 
    COALESCE(A.varname, B.varname) AS varname, 
    A.info AS info_a, 
    B.info AS info_b 
FROM 
    SETTINGS A 
    FULL OUTER JOIN SETTINGS B 
     ON A.category = 'ProductA' 
      AND B.category = 'ProductB' 
      AND A.varname = B.varname 
WHERE 
    A.varname IS NULL 
    OR B.varname IS NULL  
    OR A.info!= B.info 
ORDER BY 
    COALESCE(A.varname, B.varname) 
+0

Si vous êtes sur une plate-forme de base de données qui ne prend pas en charge CTE, vous pouvez utiliser des tables dérivées dans la requête principale où le CTE est référencé, mais il est plus «laid» et le CTE est plus lisible si vous le prenez en charge. – Toby

+0

Ah, CTE voici un outil dont je ne savais pas que j'avais besoin que je puisse voir comme très utile. COALESCE aussi. –

+0

vous pourriez utiliser un LEFT JOIN au lieu d'un JOIN FULL. La requête WITH reste la même que dans la réponse de Toby. SELECT X.category, X.varname, X.info, Y.info DE X paramètres LEFT OUTER JOIN paramètres Y = ON X.varname Y.varname ET X.category <> Y.category OÙ (X.category = 'ProductA' et X.info <> Y.info) OR Y.info EST NULL – potatopeelings

0

SELECT ... SAUF et SELECT ... Intersection se qualifier comme toujours elegan Dans mon livre, mais cela ne rend pas nécessairement le code plus clair ou plus facile à lire, et la version que j'ai trouvée contient toujours des sous-requêtes.

Basé sur la table temporaire de Paul Kearney - pk, je suis venu avec:

DECLARE 
    @Category1 varchar(32) 
,@Category2 varchar(32) 

SET @Category1 = 'ProductA' 
SET @Category2 = 'ProductB' 

SELECT isnull(set1.varname, set2.varname) varname, set1.Category, set1.Info, set2.Category, set2.Info 
from (-- Exists for "1" but not for "2" 
     select @Category1 Category, varname, info 
     from @t 
     where category = @Category1 
     except select @Category1, varname, info 
     from @t 
     where category = @Category2) set1 
    full outer join (-- Exists for "2" but not for "1" 
        select @Category2 Category, varname, info 
        from @t 
        where category = @Category2 
        except select @Category2, varname, info 
        from @t 
        where category = @Category1) set2 
    on set2.varname = set1.varname 

La jointure externe complète les prises manquantes rangées, et vous vous retrouvez avec des valeurs NULL dans la catégorie et les colonnes info .

0

Vous avez découvert l'un des nombreux problèmes liés à un modèle de données Entity-Attribute-Value. Pour les programmeurs ce modèle est très alléchant ...il vous séduit avec des promesses de simplicité et de simplicité. "Regardez, je peux ajouter de nouveaux paramètres sans DDL!" Whoa, trop cool. Mais un enregistrement dans cette table ne fait rien, vous ajoutez toujours du code pour rechercher ce paramètre, puis utilisez ce paramètre. Avec tout ce travail, est l'ajout d'une nouvelle colonne vraiment une énorme douleur?

Une table de paramètres est la seule chose que vous pourriez être en mesure d'excuser pour un EAV, mais pourquoi?

Pour répondre à votre question en vous apprenant à pêcher, au lieu de vous remettre une Fil'a'o'fish ...

Google => SQL join

les lire, mais se concentrer sur Outer et Full Outer rejoint

+0

Stephanie --- le modèle habituel pour Les réponses ici sont d'enseigner à quelqu'un à pêcher en expliquant le Fil'a'ofish qu'ils ont fait, ou au moins un squelette d'un. J'apprécie votre réponse, mais "Google this" n'est pas exactement la direction précise. –

+0

La répétition de charges d'informations redondantes sur des parties élémentaires d'un langage largement utilisé est une perte de temps et un gaspillage d'espace. C'est une direction plutôt précise lorsque j'inclus les termes de recherche EXACT à utiliser. L'utilisation de Google n'est pas difficile, sachant quels termes de recherche aboutiront à la réponse dont vous avez besoin. J'ai essayé ces termes avant de poster pour que je sache qu'ils ont travaillé. –

+0

Sérieusement, vous devez vous éloigner de ce modèle. Le plus tôt vous le faites, le mieux. Et si vous êtes convaincu que c'est le meilleur modèle pour vos données, alors passez votre SGBDR à celui qui fonctionne de cette façon. –

Questions connexes