加入优先标准?

时间:2011-06-24 14:46:55

标签: sql sql-server-2005 join

我必须将TABLE1中的每条记录与TABLE2中的最多1条记录相匹配。 有一种更好的匹配方法(CODE相等)和一个较差的方法(如果没有CODE相等,我们按CODE排序并按索引匹配)。

让我们假设这样做的代码可能是这样的第一个近似值:

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

当然,我最好获得这种行为一体化查询以避免使用多步脚本,因为:

  • 你可以随意提出一个完全不同的查询:上面的一个就是用来说明一般的想法,但很明显它不符合需要。因此,无需尝试保留其结构。

  • 与希望执行一对一查询匹配相比,我并不关心性能。如果需要子查询,那就试试吧! ; - )

渴望阅读您的建议! : - )

编辑:

我在我的OP中犯了一个很大的错误,现在已经纠正并且深刻地改变了可以被认为是准确答案的错误。预期结果不对。我最卑微的道歉。 : - (

这个想法是:尽可能多地匹配CODE相等,然后只考虑第一个匹配算法留下的那些,以便通过INDEX匹配它们。这就是错误预期INDEX与BBB匹配的AAA(已经与另一个BBB匹配),实际上必须与另一个非CODE匹配项进行INDEX匹配,在这种情况下DDD。

4 个答案:

答案 0 :(得分:1)

根据您的测试数据和预期结果,这会得到正确的结果。

;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

答案 1 :(得分:0)

我试过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

答案 2 :(得分:0)

好的,我明白了。 我发布了答案,以防有人对此感兴趣。 它可以根据最终需要进行调整,但想法就在那里(我稍微改变了字段/常量的名称:它更贴近现实生活中的名字并帮助我找到解决方案,否则它本来就太理论了)

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

答案 3 :(得分:0)

初始数据:

; 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