Поиск подходящих пар с использованием каждой записи только один раз на SQL сервере - PullRequest
5 голосов
/ 10 марта 2020

Мне нужно найти совпадающие пары записей на SQL сервере, но каждая запись может быть включена только в 1 пару . Как только запись сопоставлена ​​с парой, она должна быть удалена из рассмотрения для любых будущих пар.

Я пробовал решения, включающие ROW_NUMBER() и LEAD(), но я просто не могу туда добраться.

Это будет использоваться для объединения финансовых счетов с аналогичными счетами для проверки на основе нескольких атрибутов клиента, таких как кредитный рейтинг, доход и т. Д. c.

Отчет:

declare @test table (ID numeric, Color varchar(20))
insert into @test values
        (1,'Blue'),(2,'Red'),(3,'Blue'),(4,'Yellow'),(5,'Blue'),(6,'Red')

select* 
from @test t1
join @test t2 
    on t1.Color = t2.Color
    and t1.ID < t2.ID           -----removes reverse-pairs and self-pairs

Текущие результаты:

ID  Color   ID  Color
--- ------- --- --------
1   Blue    3   Blue
1   Blue    5   Blue        -----should not appear because 1 has already been paired
3   Blue    5   Blue        -----should not appear because 3 and 5 have already been paired
2   Red     6   Red

Необходимые результаты:

ID  Color   ID  Color
--- ------- --- --------
1   Blue    3   Blue
2   Red     6   Red

Ответы [ 2 ]

2 голосов
/ 10 марта 2020

Редактирование с максимальным количеством комментариев

Вот один из способов сделать это ..

Сначала я оцениваю записи по цвету с самым низким идентификатором с rnk = 1, следующий с rnk = 2.

После этого я объединяю таблицы, собирая записи rnk = 1 и затем соединяясь с rnk = 2.

declare @test table (ID numeric, Color varchar(20))
insert into @test values
        (1,'Blue'),(2,'Red'),(3,'Blue'),(4,'Yellow'),(5,'Blue'),(6,'Red'),(7,'Blue')

;with data
  as (select row_number() over(partition by color order by id asc) as rnk
            ,color
            ,id
       from @test
       )
select a.id,a.color,b.id,b.color
 from data a
 join data b
   on a.Color=b.Color
  and b.rnk=a.rnk+1
where a.rnk%2=1

я получаю вывод выглядит следующим образом

+----+-------+----+-------+
| id | color | id | color |
+----+-------+----+-------+
|  1 | Blue  |  3 | Blue  |
|  5 | Blue  |  7 | Blue  |
|  2 | Red   |  6 | Red   |
+----+-------+----+-------+
1 голос
/ 10 марта 2020

Вы можете использовать row_number() и условное агрегирование:

select
    max(case when rn % 2 = 0 then id end) id1,
    max(case when rn % 2 = 0 then color end) color1,
    max(case when rn % 2 = 1 then id end) id2,
    max(case when rn % 2 = 1 then color end) color2
from (
    select
        t.*,
        row_number() over(partition by color order by id) - 1 rn
    from @test t
) t
group by color, rn / 2
having count(*) = 2

Подзапрос ранжирует записи, имеющие одинаковые color, увеличивая id. Затем внешние группы запросов попарно и фильтры по группам, которые содержат две записи.

Демонстрация на DB Fiddle :

id1 | color1 | id2 | color2
:-- | :----- | :-- | :-----
1   | Blue   | 3   | Blue  
2   | Red    | 6   | Red   
...