Возвращает сопоставленную пару и несовпадающую пару для каждого идентификатора только один раз

#sql #sql-server

#sql #sql-сервер

Вопрос:

Любая помощь будет оценена.

Здесь у меня есть две примерные таблицы.

Таблица A:

 ID |Name
123|REG
123|ERT
124|REG
124|ACR
  

Таблица B

 ID |Name
123|REG
123|WWW
124|REG
124|ADR
  

Вот простой результат объединения, и я объясню свой вопрос в комментариях:

* Да — я хочу эту строку

* Нет — мне не нужна эта строка

 AID|Aname|BID|Bname
123|REG  |123|REG  --Yes-- Matched-pair for id '123'
123|ERT  |123|REG  --No--'REG' already had one match. 'ERT' should pair with 'WWW' for id '123'
123|REG  |123|WWW  --No--The same reason as above
123|ERT  |123|WWW  --Yes--non-matched pair for id '123'
124|REG  |124|REG
124|ACR  |124|REG
124|REG  |124|ADR
124|ACR  |124|ADR
  

Мой желаемый результат:

 AID|Aname|BID|Bname
123|ERT  |123|WWW
123|REG  |123|REG
124|REG  |124|REG
124|ACR  |124|ADR
  

SQL server 2017.

Заранее благодарю вас.


Мой подход (вдохновленный сообщением от @The Impaler)

 ;with CTEall as(
select A.id as AID, A.NAME as Aname, b.id as BID,b.NAME as Bname  from A
inner join B on A.id = B.id),
match as (
select A.id as AID, A.NAME as Aname, b.id as BID,b.NAME as Bname  
from A inner join B on A.id = B.id and A.NAME = B.NAME)
select *
from CTEall 
where Aname not in (select Aname from match where AID = BID) 
and Bname not in (select Aname from match where BID = AID)
union all
select * from match
order by 1
  

Комментарии:

1. Что, если во второй таблице есть два несоответствия для одного идентификатора?

2. @SalmanA во второй таблице будет только одно несоответствие.

3. @GenWan Будет ли больше строк с одинаковым идентификатором из одной таблицы или другой?

4. @alans Нет, больше строк не будет.

Ответ №1:

Часто, когда вы думаете о логике, которую вы хотите по-другому, ответ (или, по крайней мере, ответ) становится очевидным.

Я думаю о вашей логике таким образом:

ПРИСОЕДИНИТЕ таблицу A к таблице B таким образом, чтобы A.ID=B.ИДЕНТИФИКАТОР (всегда) И ЛИБО A.Name=B.Имя ИЛИ A.Name не имеет совпадения в B, и B.Name не имеет совпадения в A.

Эту логику довольно легко выразить в SQL

 WHERE a.ID=b.ID 
AND (
  a.Name=b.Name OR (
    NOT EXISTS(SELECT * FROM TableB b2 WHERE b2.ID=a.ID AND b2.Name=a.Name)
    AND 
    NOT EXISTS(SELECT * FROM TableA a2 WHERE a2.ID=b.ID AND a2.Name=b.Name)
  )
)
  

Комментарии:

1. Вот db<>fiddle для этого решения: dbfiddle.uk /… .

2. Данные примера не включают случай для нескольких несогласованных строк с каждой стороны. В этом случае этот ответ создает перекрестное соединение для них.

3. @TheImpaler это потому, что в комментарии спрашивающий говорит: «во второй таблице будет только одно несоответствие».

4. Тьфу, этого не видел. 1

Ответ №2:

Я бы сделал:

 with
m as ( -- matched rows
  select a.id as aid, a.name as aname, b.id as bid, b.name as bname
  from table_a a
  join table_b b on a.id = b.id and a.name = b.name
),
l as ( -- unmatched "left rows"
  select a.id, a.name,
    row_number() over(partition by id order by name) as rn
  from table_a a
  left join table_b b on a.id = b.id and a.name = b.name
  where b.id is null
),
r as ( -- unmatched "right rows"
  select b.id, b.name,
    row_number() over(partition by id order by name) as rn
  from table_b b
  left join table_a a on a.id = b.id and a.name = b.name
  where a.id is null
)
select aid, aname, bid, bname from m
union all
select l.id, l.name, r.id, r.name
from l
join r on r.id = l.id and r.rn = l.rn
  

Примечание: это решение может быть немного излишним, поскольку сопоставляет все несогласованные строки, когда их несколько на ID … что-то, что не обязательно. В комментариях OP всегда есть одна непревзойденная строка на ID .

Комментарии:

1. Спасибо за ваш быстрый ответ. Я думаю, что что-то не так после «объединения всех». Я не думаю, что вы можете выбрать r.id , r.name в последнем выбранном

2. Мой плохой. Исправлено использование последней строки r вместо m .

3. Ваш пост вдохновил меня. Я добавлю свой подход к резюме вопроса.