当两个表之间没有关系时,在两个表之间进行更新

时间:2011-10-11 07:12:49

标签: sql-server tsql sql-server-2008

我需要一个SQL语句,用#T2(C1)中的值填充#T1表第二列的空值。

这两个表的列之间没有外键或匹配。

样品:

T1 (C1, T2C1)
A1, 1
A2, null
A3, null
A4, 4
A5, null
-------------
T2 (C1)
a
b

更新后,T1将如下所示:

A1, 1
A2, a
A3, b
A4, 4
A5, null

我发现了两种方法:

使用CTE

create table #T1 (C1 varchar(10), T2C1 varchar(10))
create table #T2 (C1 varchar(10))

insert into #T1 values ('A1', '1')
insert into #T1 values ('A2', null)
insert into #T1 values ('A3', null)
insert into #T1 values ('A4', '4')
insert into #T1 values ('A5', null)

insert into #T2 values ('a')
insert into #T2 values ('b')

;with t2 as
(
select C1, row_number() over (order by C1) as Index2
from #T2
)
,t1 as
(
select T2C1, row_number() over (order by C1) as Index1   
from #T1
where T2C1 is null
)
update t1
set t1.T2C1 = t2.C1
from t2
where t1.Index1 = t2.Index2

select * from #T1

drop table #T1
drop table #T2

使用派生表

create table #T1 (C1 varchar(10), T2C1 varchar(10))
create table #T2 (C1 varchar(10))

insert into #T1 values ('A1', '1')
insert into #T1 values ('A2', null)
insert into #T1 values ('A3', null)
insert into #T1 values ('A4', '4')
insert into #T1 values ('A5', null)

insert into #T2 values ('a')
insert into #T2 values ('b')

update #T1
set T2C1 = cj.C1
from #T1
join (select T2C1, row_number() over (order by C1) as Index1, C1
   from #T1
   where T2C1 is null) ci on ci.C1 = #T1.C1
join (select C1, row_number() over (order by C1) as Index2
  from #T2) cj on ci.Index1 = cj.Index2

select * from #T1

drop table #T1
drop table #T2

我的问题是,我可以在不使用窗口函数且没有游标的情况下实现这一目标吗?

更新
@Damien_The_Unbeliever正确指出要进行这种更新,如果没有在表上定义排序是不可能的,实际上我认为确切地说没有正确识别和链接目标表中的行。
@Bogdan Sahlean找到了另一种方法,使用表变量和IDENTITY列,我很满意这个解决方案,这是另一种方式 但是,在实际应用程序中,我仍将使用窗口函数
谢谢大家

3 个答案:

答案 0 :(得分:1)

1.我猜你在目标表中有pk(#T1)。

2.此解决方案使用IDENTITY(1,1)列和两个表变量,而不是ROW_NUMBER。

3.我没有测试过这个解决方案。

DECLARE @t2_count INT = (SELECT COUNT(*) FROM #T2);

DECLARE @Target TABLE
(
     MyId INT IDENTITY(1,1) PRIMARY KEY
    ,T1_pk INT NOT NULL UNIQUE
);
INSERT  @Target (T1_pk)
SELECT  TOP(@t2_count) pk
FROM    #T1 
WHERE   T2C1 IS NULL;

DECLARE @Source TABLE
(
     MyId INT IDENTITY(1,1) PRIMARY KEY
    ,C1 VARCHAR(10) NOT NULL
);
INSERT  @Source (C1)
SELECT  C1
FROM    #T2;

UPDATE  #T1
SET     T2C1 = src.C1
FROM    #T1 t
INNER JOIN @Target trg ON t.pk = trg.T1_pk 
INNER JOIN @Source src ON trg.MyId = src.MyId;

答案 1 :(得分:0)

我同意Damien的意思,无论如何当你依赖引擎并假设表格是由C1列排序的(这只取决于数据库并且你不能依赖它)你可以发布更新将更新所有行的语句

declare @a int
set @a = 0

update #t1
set t2c1 = @a,
@a = @a+1

但我不会这样做。

答案 2 :(得分:0)

我会使用CTEROW_NUMBER

WITH cte_t1 AS
(
  SELECT
   T2C1
  ,ROW_NUMBER() OVER (ORDER BY C1) AS id
  FROM T1
  WHERE T2C1 IS NULL
)
,cte_t2 AS
(
  SELECT 
   C1
  ,ROW_NUMBER() OVER (ORDER BY C1) AS id
  FROM T2
)
UPDATE t1
  SET t1.T2C1 = t2.C1
  FROM cte_t1 AS t1
  INNER JOIN cte_t2 AS t2
    ON t1.id = t2.id
;

ROW_NUMBER根据列所在的顺序创建一个标识符(我假设你正在寻找它,但请注意,SQL没有订单,而是此查询依赖于记录的物理顺序,这不是一个好主意,但它看起来就像你正在处理的那样)。然后,我们加入此标识符并使​​用T2中的值更新T1中的记录。

我已将此解决方案放在SQL Fiddle上。

如果您可以查看使用某种类型的标识符创建新表,将它们链接在一起,那么您就不必依赖于记录的物理顺序。