Update in bestimmter Reihenfolge

Heute gab es im Forum mal wieder eine interessante Frage, die darauf zielte Sätze aus einer Tabelle in einer bestimmten Reihenfolge mit Sätzen aus einer anderen Tabelle zu aktualisieren.

Ausgangslage

Ich gebe hier das Posting einfach mal wieder:

Ich habe eine Tabelle, wo ich ein Feld mit den zugehrigen IDs aus einer anderen aktualisieren muss.

Die Zieltabelle sieht so aus (IDRes muss aktualisiert werden):
DNo       IDres   IDreq
10004     NULL    44725
100009    NULL    31362
100009    NULL    34573
100016    NULL    4459
100039    NULL    19639
100070    NULL    706
100089    NULL    4460
100089    NULL    45737

Die Quelltabelle so:

DNo       IDres    
10004     43121    
100009    33366  
100009    35624    
100016    2114   
100039    13804    
100070    11508    
100089    20545   
100089    45738   

Die Aufgabe ist, die IDres-Werte aus der unteren Tabelle in genau dieser Reihenfolge in die obere in das gleichnamige Feld einzufügen (zu aktualisieren).  Oder anders  ausgedrückt: Pro DNo müssen die IDres-Werte aufsteigend sortiert aus der unteren Tabelle eingefügt werden. 

Im weiteren Verlauf wurde dann noch klar, dass es kein gemeinsames Kriterium für die Reihenfolge gab und auch nicht zu jedem Satz aus der ersten Tabelle ein Wert in der zweiten Tabelle vorhanden sein musste. Diese Zeilen sollten dann NULL bleiben.

Die Lösung mit einer Window Function

An dieser Stelle greifen wir auf eine Window Function zurück, die uns seit der Version SQL Server 2008 zur Verfügung steht.

Man kommt schnell auf die Idee, dies über ROW_NUMBER() zu lösen, wobei der Trick darin besteht, diese Window Function bereits in einer Common Table Expression (CTE) anzuwenden und das UPDATE auf die CTE durchzuführen. Das funktioniert, auch wenn die per ROW_NUMBER() ermittelte Spalte natürlich nicht aktualisiert werden kann.

Ich habe die Beispieldaten entsprechend ergänzt und gebe hier das Skript für die Lösung wieder:

DECLARE @Tab1 AS TABLE (DNo integer, IDres integer, IDreq integer);
DECLARE @Tab2 AS TABLE (DNo integer, IDres integer);

INSERT INTO @Tab1(DNo, IDres, IDreq) VALUES(10004 ,   NULL ,   44725);
INSERT INTO @Tab1(DNo, IDres, IDreq) VALUES(100009,   NULL ,   31362);
INSERT INTO @Tab1(DNo, IDres, IDreq) VALUES(100009,   NULL ,   34573);
INSERT INTO @Tab1(DNo, IDres, IDreq) VALUES(100009,   NULL ,   99573);
INSERT INTO @Tab1(DNo, IDres, IDreq) VALUES(100016,   NULL ,   4459 );
INSERT INTO @Tab1(DNo, IDres, IDreq) VALUES(100039,   NULL ,   19639);
INSERT INTO @Tab1(DNo, IDres, IDreq) VALUES(100039,   NULL ,   19640);
INSERT INTO @Tab1(DNo, IDres, IDreq) VALUES(100039,   NULL ,   19650);
INSERT INTO @Tab1(DNo, IDres, IDreq) VALUES(100070,   NULL ,   706  );
INSERT INTO @Tab1(DNo, IDres, IDreq) VALUES(100089,   NULL ,   4460 );
INSERT INTO @Tab1(DNo, IDres, IDreq) VALUES(100089,   NULL ,   45737);

INSERT INTO @Tab2(DNo, IDres) VALUES(10004 ,   43121);
INSERT INTO @Tab2(DNo, IDres) VALUES(100009,   33366);
INSERT INTO @Tab2(DNo, IDres) VALUES(100009,   35624);
INSERT INTO @Tab2(DNo, IDres) VALUES(100016,   2114 );
INSERT INTO @Tab2(DNo, IDres) VALUES(100039,   13804);
INSERT INTO @Tab2(DNo, IDres) VALUES(100039,   12804);
INSERT INTO @Tab2(DNo, IDres) VALUES(100039,   11804);
INSERT INTO @Tab2(DNo, IDres) VALUES(100070,   11508);
INSERT INTO @Tab2(DNo, IDres) VALUES(100089,   20545);
INSERT INTO @Tab2(DNo, IDres) VALUES(100089,   45738);

SELECT DNo, IDres, IDReq, ROW_NUMBER() OVER(PARTITION BY DNo ORDER BY IDReq) AS rrn
FROM @Tab1
ORDER BY DNo, IDreq;

SELECT DNo, IDres, ROW_NUMBER() OVER(PARTITION BY DNo ORDER BY IDRes) AS rrn
FROM @Tab2
ORDER BY DNo, IDres;

WITH T1 AS
(SELECT DNo, IDres, IDReq, ROW_NUMBER() OVER(PARTITION BY DNo ORDER BY IDReq) AS rrn
FROM @Tab1
),
T2 AS
(SELECT DNo, IDres, ROW_NUMBER() OVER(PARTITION BY DNo ORDER BY IDRes) AS rrn
FROM @Tab2
)
UPDATE T1
SET IDres = T2.IDres
FROM T1 INNER JOIN T2
ON T1.DNo = T2.DNo
AND T1.rrn = T2.rrn;

SELECT *
FROM @Tab1
ORDER BY DNo, IDreq;