SQL: найти строки, где значение поля отличается - PullRequest
2 голосов
/ 05 июня 2009

У меня есть таблица базы данных, структурированная так (нерелевантные поля для краткости опущены):

rankings
------------------
(PK) indicator_id
(PK) alternative_id
(PK) analysis_id
rank

Все поля являются целыми числами; первые три (обозначенные «(PK)») являются составным первичным ключом. Данный «анализ» имеет несколько «альтернатив», каждая из которых будет иметь «ранг» для каждого из множества «показателей».

Я ищу эффективный способ сравнения произвольного числа анализов, чьи ранги для любой комбинации альтернатив / индикаторов отличаются. Так, например, если у нас есть эти данные:

analysis_id | alternative_id | indicator_id | rank
----------------------------------------------------
          1 |              1 |            1 |    4
          1 |              1 |            2 |    6
          1 |              2 |            1 |    3
          1 |              2 |            2 |    9
          2 |              1 |            1 |    4
          2 |              1 |            2 |    7
          2 |              2 |            1 |    4
          2 |              2 |            2 |    9

... тогда идеальный метод определил бы следующие различия:

analysis_id | alternative_id | indicator_id | rank
----------------------------------------------------
          1 |              1 |            2 |    6
          2 |              1 |            2 |    7
          1 |              2 |            1 |    3
          2 |              2 |            1 |    4

Я пришел с запросом, который выполняет то, что я хочу, для 2 идентификаторов анализа, но у меня возникают проблемы с обобщением, чтобы найти различия между произвольным числом идентификаторов анализа (то есть пользователь может захотеть сравнить 2 или 5 или 9, или что-то еще, и найдите строки, где хотя бы один анализ отличается от любого другого). Мой запрос:

declare @analysisId1 int, @analysisId2 int;
select @analysisId1 = 1, @analysisId2 = 2;

select 
    r1.indicator_id, 
    r1.alternative_id,
    r1.[rank] as Analysis1Rank,
    r2.[rank] as Analysis2Rank
from rankings r1
inner join rankings r2
    on r1.indicator_id = r2.indicator_id
        and r1.alternative_id = r2.alternative_id
        and r2.analysis_id = @analysisId2
where
    r1.analysis_id = @analysisId1
    and r1.[rank] != r2.[rank]

(Он помещает значения анализа в дополнительные поля вместо строк. Я думаю, что в любом случае будет работать.)

Как я могу обобщить этот запрос для обработки многих идентификаторов анализа? (Или, в качестве альтернативы, придумайте другой, лучший запрос для выполнения этой работы?) Я использую SQL Server 2005 на случай, если это имеет значение.

При необходимости я всегда могу вытащить все данные из таблицы и посмотреть на различия в коде, но решение на SQL предпочтительнее, так как часто мне нужно заботиться только о нескольких строках из тысяч, и нет смысла передавая их все, если я могу избежать этого. (Однако, если у вас есть веская причина не делать этого в SQL, скажите так - я тоже считаю, что это хороший ответ!)

Ответы [ 4 ]

2 голосов
/ 05 июня 2009

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

    select r.* from rankings r
    inner join
    (
        select alternative_id, indicator_id
        from rankings
        group by alternative_id, indicator_id
        having count(distinct rank) > 1
    ) differ on r.alternative_id = differ.alternative_id
    and r.indicator_id = differ.indicator_id
    order by r.alternative_id, r.indicator_id, r.analysis_id, r.rank
1 голос
/ 05 июня 2009

Я не знаю, какую базу данных вы используете, в SQL Server я бы сказал так:

-- STEP 1, create temporary table with all the alternative_id , indicator_id combinations with more than one rank:
select alternative_id , indicator_id
into #results
from rankings 
group by alternative_id , indicator_id
having count (distinct rank)>1

-- STEP 2, retreive the data

select a.* from rankings a, #results b
where a.alternative_id  = b.alternative_id
and  a.indicator_id = b. indicator_id
order by alternative_id , indicator_id, analysis_id 

Кстати, другие ответы, приведенные здесь, нуждаются в подсчете (отдельный ранг) !!!!!

0 голосов
/ 05 июня 2009

Ваш пример различий кажется неверным. Вы говорите, что хотите анализы, чьи ранги для любой комбинации альтернатив / индикаторов отличаются , но строки примеров 3 и 4 не удовлетворяют этому критерию. Правильный результат по вашему требованию:

 analysis_id | alternative_id | indicator_id | rank
 ----------------------------------------------------
      1 |              1 |            2 |    6
      2 |              1 |            2 |    7
      1 |              2 |            1 |    3
      2 |              2 |            1 |    4

По запросу вы можете попробовать это:

with distinct_ranks as (
    select alternative_id  
    , indicator_id
    , rank
    , count (*) as count
    from rankings
        group by alternative_id  
        , indicator_id
        , rank
    having count(*) = 1)
select r.analysis_id
    , r.alternative_id  
    , r.indicator_id
    , r.rank
from rankings r
    join distinct_ranks d on r.alternative_id = d.alternative_id
        and r.indicator_id = d.indicator_id
        and r.rank = d.rank

Вы должны понимать, что при множественном анализе ваши критерии неоднозначны. Что если анализ 1,2 и 3 имеют ранг 1, а 4,5 и 6 имеют ранг 2 для альтернативы / показателя 1/1? Набор (1,2,3) «отличается» от набора (4,5,6), но внутри каждого набора нет никакой разницы. Какое поведение вы хотите в этом случае, они должны появиться или нет? Мой запрос находит все записи, которые имеют другой ранг для той же альтернативы / индикатора * из всех других анализов ', но не ясно, верно ли это в вашем требовании.

0 голосов
/ 05 июня 2009

Я думаю, это то, что вы пытаетесь сделать:

select 
    r.analysis_id, 
    r.alternative_id, 
    rm.indicator_id_max,
    rm.rank_max
from rankings rm
    join (
        select 
            analysis_id, 
            alternative_id, 
            max(indicator_id) as indicator_id_max, 
            max(rank) as rank_max 
        from rankings 
        group by analysis_id, 
            alternative_id 
        having count(*) > 1
    ) as rm
    on r.analysis_id = rm.analysis_id
    and r.alternative_id = rm.alternative_id
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...