ПРИСОЕДИНИТЬСЯ с приоритетными критериями? - PullRequest
1 голос
/ 24 июня 2011

Я должен сопоставить каждую запись в TABLE1 с максимум 1 записью в TABLE2.Существует лучший способ сопоставления (равенство кодов) и плохой (в случае отсутствия равенства кодов, давайте отсортируем по кодам и сопоставим по индексу).

Давайте в качестве первого приближения предположим, что код делает этоможет выглядеть так:

SELECT
TABLE1.CODE AS CODE1,
TABLE2.CODE AS CODE2
FROM 
(SELECT ROW_NUMBER() OVER(ORDER BY CODE) INDEX, CODE FROM TABLE1) T1
LEFT JOIN
(SELECT ROW_NUMBER() OVER(ORDER BY CODE) INDEX, CODE FROM TABLE2) T2
ON
(T1.CODE=T2.CODE) --CODE equality
OR
(T1.INDEX=T2.INDEX) --CODE equality

Давайте рассмотрим эти таблицы:

 TABLE1   TABLE2
+------+ +------+
| CODE | | CODE |
+------+ +------+
| AAA  | | BBB  |
| BBB  | | CCC  |
| CCC  | | DDD  |
+------+ +------+

Результат будет:

CODE1 CODE2
----- -----
AAA   BBB    -> matched because of INDEX equality
BBB   BBB    -> matched because of CODE equality
BBB   CCC    -> matched because of INDEX equality
CCC   CCC    -> matched because of CODE equality
CCC   DDD    -> matched because of INDEX equality

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

Требуемый результат:

CODE1 CODE2
----- -----
AAA   DDD    -> matched because of INDEX equality between the cast-off records not able to match better
                (corrected from the previous version where AAA was said to match expectedly with BBB)
BBB   BBB    -> matched thanks to CODE equality, no need to match on INDEX
CCC   CCC    -> matched thanks to CODE equality, no need to match on INDEX

И, конечно, я бы предпочел получить такое поведение все-в-одном, чтобы избежать сценария из нескольких шагов, учитывая, что:

  • вы можете свободно предлагать совершенно другой запрос: приведенный выше был просто для иллюстрации общей идеи, но ясно, что он не соответствует потребностям.Таким образом, нет необходимости пытаться сохранить его структуру.

  • Меня не очень интересует производительность по сравнению с желанием выполнить сопоставление по запросу "все в одном".Если нужны подзапросы, поехали!; -)

Ждем ваших предложений!: -)

РЕДАКТИРОВАТЬ:

Я допустил серьезную ошибку в своем ОП, которая теперь исправлена ​​и глубоко меняет то, что можно считать точным ответом.Ожидаемые результаты не были правильными.Мои самые скромные извинения.: - (

Идея такова: сопоставьте как можно больше по равенству КОДОВ, а затем рассмотрите только те, которые остались в этом первом алгоритме сопоставления, чтобы сопоставить их по индексу. Вот почему AAA, который неправильно ожидалсяINDEX-совпадение с BBB (которое уже CODE-сопоставляет с другим BBB), должно фактически INDEX-совпадать с другим элементом, не совпадающим с CODE, в этом случае DDD.

Ответы [ 4 ]

1 голос
/ 24 июня 2011

Учитывая ваши данные испытаний и ожидаемые результаты, это дает правильные результаты.

;WITH T1 (row_id, code) AS (SELECT ROW_NUMBER() OVER (ORDER BY code) AS row_id, code FROM My_Table_1),
     T2 (row_id, code) AS (SELECT ROW_NUMBER() OVER (ORDER BY code) AS row_id, code FROM My_Table_2)
SELECT
    T1.code,
    COALESCE(T2.code, T3.code)
FROM
    T1
LEFT OUTER JOIN T2 ON T2.code = T1.code
LEFT OUTER JOIN T2 AS T3 ON T2.row_id IS NULL AND T3.row_id = T1.row_id
0 голосов
/ 01 июля 2011

Исходные данные:

; WITH 

  T1 (CODE) AS
  ( SELECT 'AAA' CODE UNION
    SELECT 'BBB'      UNION 
    SELECT 'CCC'
  )
, T2 (CODE) AS
  ( SELECT 'BBB' CODE UNION 
    SELECT 'CCC'      UNION 
    SELECT 'DDD' 
  )

Справочные таблицы:

 , FULLT (code1, code2) AS
   ( SELECT T1.CODE AS code1
          , T2.CODE AS code2
     FROM T1
       FULL OUTER JOIN T2
         ON T1.CODE = T2.CODE 
   )
 , INNERT (code1, code2) AS
   ( SELECT code1
          , code2
     FROM FULLT
     WHERE code1 = code2 
   )
 , LEFTT (code1, rn) AS
   ( SELECT code1
          , ROW_NUMBER() OVER(ORDER BY code1) AS rn
     FROM FULLT
     WHERE code2 IS NULL
   )
 , RIGHTT (code2, rn) AS
   ( SELECT code2
          , ROW_NUMBER() OVER(ORDER BY code2) AS rn
     FROM FULLT
     WHERE code1 IS NULL
   ) 

Окончательный запрос:

   SELECT code1             
        , code2
   FROM INNERT 

 UNION ALL

   SELECT code1
        , code2
   FROM LEFTT
     JOIN RIGHTT
       ON LEFTT.rn = RIGHTT.rn 

 ORDER BY code1
        , code2 
0 голосов
/ 01 июля 2011

Хорошо, я понял.Я выкладываю ответ на тот случай, если он кому-то может быть интересен.Его можно настроить в зависимости от конечных потребностей, но идея в этом есть (я немного изменил имена полей / констант: они ближе к реальным именам и помогли мне найти решение, иначе было бы слишком теоретически).

declare @uc table(cod_uc varchar(3))
declare @cnt table(cod_cnt varchar(3))

insert into @uc values('a')
insert into @uc values('b')
insert into @uc values('c')
insert into @cnt values('b')
insert into @cnt values('c')
insert into @cnt values('d')
insert into @cnt values('e')

;with codematchings(cod_uc,cod_cnt)
as
(a
    select uc.cod_uc, cnt.cod_cnt
    from 
    @uc uc full outer join @cnt cnt on uc.cod_uc=cnt.cod_cnt
),
orders(cod_uc,order_uc,cod_cnt,order_cnt)
as
(
    select cod_uc,row_number() over(order by isnull(cod_uc,'zzzz')) order_uc, cod_cnt,row_number() over(order by isnull(cod_cnt,'zzzz')) order_cnt from codematchings where cod_uc is null or cod_cnt is null
)
select codematchings.*,
case
when codematchings.cod_uc is null then orders2.cod_uc
when codematchings.cod_cnt is null then orders2.cod_cnt
else codematchings.cod_uc
end matched_with
from codematchings
left outer join orders orders1
on codematchings.cod_uc is null and orders1.cod_cnt=codematchings.cod_cnt or codematchings.cod_cnt is null and orders1.cod_uc=codematchings.cod_uc
left outer join orders orders2
on codematchings.cod_uc is null and orders2.order_uc=orders1.order_cnt or codematchings.cod_cnt is null and orders2.order_cnt=orders1.order_uc

Результат:

cod_uc cod_cnt matched_with
------ ------- ------------
A      NULL    D
B      B       B
C      C       C
NULL   D       A
NULL   E       NULL
0 голосов
/ 24 июня 2011

Я пробовал на Oracle, но идея здесь:

with t1 as (select 1 id,'AAA' col from dual union all
            select 2,   'BBB'     from dual union all
            select 3,   'CCC'     from dual ),
     t2 as (select 1 id,'BBB' col from dual union all
            select 2,   'CCC'     from dual union all
            select 3,   'DDD'     from dual )
---
SELECT t1.col col1, t2.col col2
  FROM t1, t2
 WHERE (t1.id = t2.id OR t1.col = t2.col)
   AND (t2.id = 1 OR t1.col = t2.col)

результаты:

COL1 COL2
---  ---
AAA  BBB
BBB  BBB
CCC  CCC
...