递归更新声明

时间:2011-12-08 13:58:43

标签: sql sql-server

我需要创建一个递归更新语句,从另一个表更新,以便ex ..

    Table1
    (
       IdNumberGeneratedFromAService INT NOT NULL,
       CodeName NVARCHAR(MAX)
    )

    Table2 
    (
       Table2Id Auto_Increment,
       Name NVARCHAR(MAX),
       IdNumberThatComesFromTabl1,
       CodeNameForTable1ToMatch
    )

问题是CodeNameForTable1ToMatch不是唯一的,所以如果Table1对于相同的代码有2个idnumber并且Table2中有两行具有相同的CodeName我想要按顺序更新table2中的行,所以第一行得到第一个idnumber和第二行row获取第二个id号。

也想在没有光标的情况下这样做....

SAMPLE DATA

Table1
idNumber            Code
C145-6678-90        Code1
C145-6678-91        Code1
C145-6678-92        Code1
C145-6678-93        Code1   
C145-6678-94        Code1


Table 2
AutoIncrementIdNumber       Code        IdNumber
1               Code1       {NULL}
2               Code1       {NULL}
3               Code1       {NULL}
4               Code1       {NULL}
5               Code1       {NULL}


C145-6678-90 needs to got 1
C145-6678-91 needs to got 2
C145-6678-92 needs to got 3
C145-6678-93 needs to got 4
C145-6678-94 needs to got 5

in one update statement

3 个答案:

答案 0 :(得分:6)

在每个表上使用ROW_NUMBER窗口函数,按代码分区,您可以对每个具有共同代码的行进行编号,然后将每个查询的结果组合在一起,以根据代码和行匹配行该代码的编号实例。因此,表1中的第一个代码A将匹配表2中的第一个代码A,等等。

显示此示例代码(SQL 2005或更高版本):

-- Sample code prep
CREATE TABLE #Table1
(
   IdNumberGeneratedFromAService INT NOT NULL,
   CodeName NVARCHAR(MAX)
);

CREATE TABLE #Table2 
(
   Table2Id INT NOT NULL IDENTITY(1,1),
   Name NVARCHAR(MAX),
   IdNumberThatComesFromTabl1 INT NULL,
   CodeNameForTable1ToMatch  NVARCHAR(MAX)
);


INSERT INTO #Table1(IdNumberGeneratedFromAService, CodeName)
VALUES(100,'Code A'),(150,'Code A'),(200,'Code B'),(250,'Code A'),(300,'Code C'),(400,'Nonexistent');

INSERT INTO #Table2(Name, IdNumberThatComesFromTabl1, CodeNameForTable1ToMatch)
VALUES('A1-100',0,'Code A'),('A2-150',0,'Code A'),('A3-250',0,'Code A'),('B1-200',0,'Code B'),('C1-300',0,'Code C'),('No Id For Me',0,'Code No Id :(');

-- Sample select statement that shows the row numbers
--SELECT * 
--FROM
--  (SELECT *, ROW_NUMBER() OVER (Partition By IT2.CodeNameForTable1ToMatch Order By IT2.Table2Id) as RowNum
--      FROM #Table2 IT2) T2
--  INNER JOIN
--  (SELECT *, ROW_NUMBER() OVER (Partition By IT1.CodeName Order By IT1.IdNumberGeneratedFromAService) as RowNum
--      FROM #Table1 IT1) T1
--      ON T1.CodeName = T2.CodeNameForTable1ToMatch AND T1.RowNum = T2.RowNum;

-- Table 2 Before
SELECT * FROM #Table2;

-- Actual update statement
UPDATE #Table2
SET IdNumberThatComesFromTabl1 = T1.IdNumberGeneratedFromAService
FROM #Table2 AT2
INNER JOIN
    (SELECT *, ROW_NUMBER() OVER (Partition By IT2.CodeNameForTable1ToMatch Order By IT2.IdNumberThatComesFromTabl1) as RowNum
        FROM #Table2 IT2) T2
    ON T2.Table2Id = AT2.Table2Id
INNER JOIN 
    (SELECT *, ROW_NUMBER() OVER (Partition By IT1.CodeName Order By IT1.IdNumberGeneratedFromAService) as RowNum
        FROM #Table1 IT1) T1
        ON T1.CodeName = T2.CodeNameForTable1ToMatch AND T1.RowNum = T2.RowNum;

-- Table 2 after
SELECT * FROM #Table2;

-- Cleanup
DROP TABLE #Table1;
DROP TABLE #Table2;

我将两个样本表转换为临时表,并为“代码A”添加了3条记录,为“代码B”添加了记录,并为“代码C”添加了记录。表1中的代码根据表1 ID的顺序编号,表2中的代码按自动递增表2 id排序。我还在每个表中包含了一条记录,在另一个表中没有匹配。我试图使代码具有描述性,因此更容易看到发生了正确的匹配(因为它具有自动递增ID,所以它们对表2的顺序很重要)

注释掉的示例选择用于帮助理解选择在我将其加入UPDATE语句之前的工作方式。

因此我们可以在更新之前看到表2全部为0,然后我们更新表2中的值,其中唯一表2的id与我们编号良好且匹配的连接中的唯一表2 id匹配,然后我们从表2中选择再看看结果。

答案 1 :(得分:1)

对Tarwn的解决方案进行了一次试验:

with cte1 as (
   select code, row_number() over (partition by code order by idNumber) as [rn]
   from table1
), cte2 as (
   select code, row_number() over (partition by code order by AutoIncrementIdNumber) as [rn]
   from table2 
)
update cte2
set idNumber = cte1.idNumber
from cte2
inner join cte1
   on cte2.code = cte1.code
   and cte2.rn = cte1.rn

我只是提出这个问题,因为人们常常惊讶于你可以更新公用表表达式。

答案 2 :(得分:-3)

没有光标,这是不可能的。