web-dev-qa-db-fra.com

Mise à jour SQL de plusieurs lignes dans la table de destination avec le même identifiant mais des valeurs différentes de la table source

J'ai deux tables différentes avec une colonne commune appelée id:

Table1
----  -------
id  |  Date
----  -------
1      null
1      null
2      null
2      null
2      null
2      null
3      null
4      null
4      null

Table2
----  -------
id  |  Date
----  -------
1      2013-01-29 08:50:00.000
1      2013-01-29 15:28:00.000
2      2013-01-31 11:56:00.000
2      2013-03-11 16:08:00.000
2      2013-01-31 14:04:00.000
2      2013-01-31 14:08:00.000
3      2013-02-28 23:44:00.000
4      2013-01-31 14:04:00.000
4      2013-01-31 14:08:00.000

J'ai besoin d'écrire une déclaration de mise à jour qui mettra essentiellement la valeur de la première ligne avec id = 1 de Table2 en Table1 avec un id = 1 et la deuxième ligne avec id = 1 de Table2 en Table1 avec un id = 1 pour que Table1 ressemble maintenant à ceci

Table1
----  -------
id  |  Date
----  -------
1      2013-01-29 08:50:00.000
1      2013-01-29 15:28:00.000

Actuellement, cette instruction SQL

UPDATE Table1
  SET Table1.date = T2.date
  FROM 
  Table2 T2
  INNER JOIN Table1 T1
  ON T1.date = T2.date

est en train de faire Table1 ressemble à ceci, où il applique la première date qu'il trouve pour cet identifiant à toutes les lignes avec des identifiants correspondants dans la table de destination

Table2
----  -------
id  |  Date
----  -------
1      2013-01-29 08:50:00.000
1      2013-01-29 08:50:00.000

Existe-t-il un moyen de mettre à jour Table1 pour ressembler à ceci ci-dessous:

Table1
----  -------
id  |  Date
----  -------
1      2013-01-29 08:50:00.000
1      2013-01-29 15:28:00.000
2      2013-01-31 11:56:00.000
2      2013-03-11 16:08:00.000
2      2013-01-31 14:04:00.000
2      2013-01-31 14:08:00.000
3      2013-02-28 23:44:00.000
4      2013-01-31 14:04:00.000
4      2013-01-31 14:08:00.000

Existe-t-il un moyen de le faire dans une instruction SQL? Je ne veux pas utiliser de préférence un curseur.

Il y a d'autres colonnes dans les deux tables et pas toutes les valeurs de id dans Table1 pourrait être présent dans Table2 et vice versa. Ces autres lignes et colonnes que je ne veux ni toucher ni modifier.

4
user20358

Impossible de trouver un moteur de violon SQL Server 2008, j'ai donc dû opter pour un serveur SQL Server 2014 ... donc je ne sais pas si les éléments suivants fonctionneront dans SQL Server 2008, mais fwiw ...

Configurez quelques exemples de données:

create table Table1(id int, Date datetime null);
create table Table2(id int, Date datetime);

insert Table1 values (1,null)
insert Table1 values (1,null)
insert Table1 values (2,null)
insert Table1 values (2,null)
insert Table1 values (2,null);

insert Table2 values (1,'2013-01-29 08:50:00.000')
insert Table2 values (1,'2013-01-29 15:28:00.000')
insert Table2 values (2,'2013-01-31 11:56:00.000')
insert Table2 values (2,'2013-03-11 16:08:00.000')
insert Table2 values (2,'2013-01-31 14:04:00.000');

En gardant à l'esprit que nous n'avons pas (encore) fourni de moyen pour déterminer les lignes à faire correspondre entre Table1 et Table2 pour une valeur d'ID donnée, je laisserai simplement row_number () générer un ID de ligne `` correspondant ''.

Et puis nous utiliserons la capacité de SQL Server à mettre à jour Table1 via une définition de table dérivée:

update T1 
set    T1.Date=T2.Date

from   (select row_number() over(partition by id order by Date) as rowid,
               id,
               Date
        from   Table1 
        where  Date is NULL) T1

join   (select row_number() over(partition by id order by Date) as rowid,
               id,
               Date
        from   Table2) T2

on      T1.id    = T2.id
and     T1.rowid = T2.rowid;

Et les résultats:

select * from Table1;

id  Date
--- --------------------
1   2013-01-29T08:50:00Z
1   2013-01-29T15:28:00Z
2   2013-01-31T11:56:00Z
2   2013-01-31T14:04:00Z
2   2013-03-11T16:08:00Z

Et voici un SQL Fiddle pour ce qui précède.

8
markp-fuso

Vous avez déclaré que l'ordre des correspondances est important, mais il semble que vous n'ayez rien à ORDER BY dans le tableau 1 pour créer un ordre garanti pour correspondre à l'autre table et il n'y a aucun moyen dans SQL Server de classer les lignes après la date d'insertion , car les informations à ce sujet ne sont pas stockées. Dans cet esprit, il n'est pas possible de faire une correspondance avec le résultat souhaité. Il existe une solution pour mettre à jour les lignes avec une correspondance arbitraire dans chaque identifiant. Si cela pouvait suffire.

UPDATE t 
SET    t.[date] = tt.[date] 
FROM   (SELECT *, 
               Row_number() 
                 OVER ( 
                   partition BY id 
                   ORDER BY [date]) AS rno 
        FROM   Table1) AS t 
       INNER JOIN (SELECT *, 
                          Row_number() 
                            OVER ( 
                              partition BY id 
                              ORDER BY [date]) AS rno 
                   FROM   Table2) AS tt 
               ON t.id = tt.id 
                  AND t.rno = tt.rno 

Cette solution correspondra à toutes les lignes individuellement mais ne peut garantir la commande.

DB Fiddle

3
Daniel Björk