web-dev-qa-db-ja.com

SQLは宛先テーブルの複数の行を同じIDでソーステーブルとは異なる値で更新します

Idという共通の列を持つ2つの異なるテーブルがあります。

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

基本的に、Table2のid = 1の最初の行の値をid = 1のTable1に入れ、Table2のid = 1の2番目の行の値をid = 1のTable1に入れて、Table1にする今のように見えます

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

現在、このSQLステートメント

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

Table1を次のようにして、そのIDで最初に見つかった日付を、宛先テーブルのIDが一致するすべての行に適用します

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

Table1を以下のように更新する方法はありますか?

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

SQLステートメントでこれを行う方法はありますか?できればカーソルを使いたくない。

両方のテーブルに他の列があり、Table1のすべてのid値がTable2にあるとは限りません。逆も同様です。触れたり変更したりしたくない他の行と列。

4
user20358

SQL Server 2008のフィドルエンジンが見つからなかったため、SQL Server 2014を選択する必要がありました。そのため、以下がSQL Server 2008で機能するかどうかはわかりませんが、fwiw ...

いくつかのサンプルデータをセットアップします。

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');

与えられたid値に対してTable1とTable2の間でどの行を一致させるかを決定する手段が(まだ)提供されていないことに留意して、row_number()に「一致する」行IDを生成させます。

次に、SQL Serverの機能を利用して、派生テーブル定義を介してTable1を更新します。

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;

そして結果:

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

そして、これが上記の SQL Fiddle です。

8
markp-fuso

一致の順序が重要であると述べましたが、他のテーブルと一致する保証された順序を作成するために、テーブル1のORDER BYに何もないようで、SQL Serverには挿入日後に行を順序付ける方法がありません。 、それに関する情報は保存されないため。これを念頭に置いて、希望する結果と照合することはできません。各ID内の任意の一致で行を更新するソリューションがあります。それで十分なら。

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 

このソリューションはすべての行を個別に照合しますが、順序を保証することはできません。

DBフィドル

3
Daniel Björk