如何优雅地比较SQL中同一个表中的值

时间:2010-06-22 01:38:33

标签: sql tsql database-design

我有一个设置表,用'category'构造确定不同的产品,'varname'是变量名,'info'是设置的值。

所以,例如,

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

结果:

 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  

当category ='ProductA'和'ProductB'时,我想生成一个简单的值之间的差异列表。我可以想办法用临时表或一些子选择(例如,这个痛苦的子句)来做到这一点:

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

但是当b中的varname不在a中时,上述方法(至少)会失败。 (任何解决方案都应该解决这个问题,a和b之间的varnames的任何差异都应该表示出来。)

这不是一个难以解决的难题,但是我想知道是否有一种'正确的方法'可以优雅地完成这项工作,没有可怕的子选择或没有上述警告。

这与SQL无关,但这个特定的表位于MSSQL服务器中。

谢谢, RK

5 个答案:

答案 0 :(得分:2)

您可以使用自联接来完成此任务:

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

以下是我用来测试的脚本:

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

答案 1 :(得分:2)

如果您只关心varname和info值,可以执行以下操作:

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'
        )

如果您想要源表中的其他列,那么您可以执行以下操作:

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'

然而第三种方法是简单地使用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
                    )

答案 2 :(得分:1)

我想要使用CTE和完整的外部联接,我想:

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)

答案 3 :(得分:0)

SELECT ... EXCEPT和SELECT ... INTERSECT在我的书中总是有资格作为优雅,但这并不一定使代码更整洁或更容易阅读,我想出的版本仍然包含子查询。

基于 Paul Kearney -pk 的临时表,我想出了:

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

完整的外部联接会捕获缺少的行,并且在“类别”和“信息”列中最终会出现一些NULL。

答案 4 :(得分:0)

您发现了实体 - 属性 - 值数据模型的众多问题之一。对于程序员来说,这个模型非常诱人......它以轻松和简单的承诺引诱你。 “看,我可以添加一个没有DDL的新设置!”哇,太酷了。但是该表中的记录没有做任何事情,您仍然在添加代码来查找该设置,然后使用该设置。有了这些工作,添加新专栏真的是一个巨大的痛苦吗?

设置表是你唯一能够为EAV辩解的东西,但为什么呢?

通过教你捕鱼来回答你的问题,而不是递给你一个Fil'a'o'fish ......

Google =&gt; SQL JOIN TYPES

全部阅读,但重点是外部和全部外部连接