Как элегантно сравнить значения в одной таблице в TSQL - PullRequest
3 голосов
/ 22 июня 2010

У меня есть таблица настроек, построенная из категории, определяющей различные продукты, 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 есть имя, которого нет в a. (Любые решения должны решить эту проблему, любые различия в именах переменных между a и b также должны быть представлены.)

Это не сложная проблема, которую трудно решить хитроумно, но мне интересно, есть ли «правильный» способ сделать это элегантно, без ужасных подвыборов или без оговорок выше.

Это более независимый от SQL, но эта конкретная таблица находится на сервере MSSQL.

Спасибо, Rk

Ответы [ 5 ]

2 голосов
/ 22 июня 2010

Если вы заботитесь только о значениях 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 голосов
/ 22 июня 2010

Вы можете использовать самостоятельное соединение, чтобы выполнить это:

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 голос
/ 22 июня 2010

Вы хотите использовать для этого 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)
0 голосов
/ 22 июня 2010

Вы обнаружили только одну из множества проблем с моделью данных Entity-Attribute-Value. Для программистов эта модель очень заманчива ... она соблазняет вас обещаниями легкости и простоты. «Смотри, я могу добавить новые настройки без DDL!» Вау, тоже круто Но запись в этой таблице ничего не делает, вы все еще добавляете код для поиска этого параметра, а затем используете этот параметр. При всей этой работе добавление нового столбца действительно вызывает огромную боль?

Таблица настроек - это единственное, что вы МОЖЕТЕ извинить за EAV, но почему?

Чтобы ответить на ваш вопрос, научив вас ловить рыбу, вместо того, чтобы вручить вам Fil'a'o'fish ...

Google => ТИПЫ СОЕДИНЕНИЯ SQL

Прочитайте их все, но сосредоточьтесь на Внешних и Полных Внешних объединениях

0 голосов
/ 22 июня 2010

SELECT ... EXCEPT и SELECT ... INTERSECT всегда квалифицируются как элегантные в моей книге, но это не обязательно делает код аккуратнее или проще для чтения, а версия, которую я придумал, все еще содержит подзапросы.

На основании временной таблицы из Пол Керни - 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 в столбцах Category и Info.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...