使用每行一次的完全外部联接

时间:2012-06-06 15:03:21

标签: sql sql-server join outer-join

我想知道是否有人遇到过这个问题的简洁解决方案。我正在尝试从几个表中选择数据,让记录逐行匹配。我基本上是在完全外连接之后,但是有一个关键的区别。如果我在一个表中连接的列中有四个特定值的行,而另一个表中有三个具有此值的行,我只希望连接前三个结果,并且第四个行为就好像有一直不配。

这样做的原因是创建一个对帐报告,确保在比较结果时不会多次计算交易。我可以通过使用一些分组和一些聚合函数来解决这个问题,但这隐藏了一些我想保留的细节。

下面是一个示例,显示我正在追求的事情,评论中的无效/伪代码说明了我如何将其视为工作:

declare @t1 table (id bigint identity(1,1) primary key clustered, foreignKeyId bigint, otherData nvarchar(10))
declare @t2 table (id bigint identity(1,1) primary key clustered, foreignKeyId bigint, moreData nvarchar(10))

insert @t1 select 1, '1.1.1'
 union all select 1, '1.1.2'
 union all select 1, '1.1.3'
 union all select 3, '1.3.1'
 union all select 3, '1.3.2'
 union all select 3, '1.3.3'
 union all select 4, '1.4.3'

insert @t2 select 1, '2.1.1'
 union all select 1, '2.1.2'
 union all select 1, '2.1.3'
 union all select 2, '2.2.1'
 union all select 3, '2.3.1'
 union all select 3, '2.3.2'
 union all select 5, '2.5.1'
 union all select 5, '2.5.2'

--demo of the functionality i'm hoping to acheive 
--
 /*
 select t1.id           id1
 , t2.id                id2
 , t1.foreignKeyId  fk1
 , t2.foreignKeyId  fk2
 , t1.otherData     otherData
 , t2.moreData      moreData
 from @t1 t1
 full funky join @t2 t2
 on t1.foreignKeyId = t2.foreignKeyId
 order by t1.id, t2.id --we'd need an order by to ensure the match could be applied in a predictable manner
 */
--
declare @funkyjoin table (id1 bigint, id2 bigint, fk1 bigint, fk2 bigint, otherData nvarchar(10), moreData nvarchar(10))
declare @id1 bigint, @id2 bigint
insert @funkyjoin (id1, fk1, otherData) 
select id, foreignKeyId, otherData from @t1

while exists(select 1 from @t2)
begin
    select top 1 @id2 = id from @t2 order by id 

    set @id1 = null

    select top 1 @id1 = id1 
    from @funkyjoin
    where fk2 is null
    and fk1 in (select foreignKeyId from @t2 where id = @id2)

    if @id1 is null
    begin
        insert @funkyjoin (id2, fk2, moreData)
        select id, foreignKeyId, moreData
        from @t2
        where id = @id2
    end
    else
    begin
        update @funkyjoin
        set id2 = @id2
        , fk2 = fk1 --since we're joining on this we can just match it
        , moreData = (select moreData from @t2 where id = @id2)
        where id1 = @id1 
    end

    delete from @t2 where id = @id2 --since this is only an example let's not worry about keeping our source data
end

select * 
from @funkyjoin
order by coalesce(id1, id2)

我之前在电子表格上发生过这种情况的时候写过类似的解决方案:http://officemacros.codeplex.com/#WorksheetMergeMacro

2 个答案:

答案 0 :(得分:3)

如果我理解正确,这可能就是你所追求的:

select *
from (
  select *,
    row_number() over (partition by foreignKeyId order by id) as n
  from @t1
) t1
full outer join (
  select *,
    row_number() over (partition by foreignKeyId order by id) as n
  from @t2
) t2 on t1.foreignKeyId = t2.foreignKeyId and t1.n = t2.n

答案 1 :(得分:1)

使用行的最佳方法是添加伪行号(使用ROW_NUMBER)并将其包含在连接中。