Oracle SQL:別のテーブルのデータでテーブルを更新する


251

表1:

id    name    desc
-----------------------
1     a       abc
2     b       def
3     c       adf

表2:

id    name    desc
-----------------------
1     x       123
2     y       345

Oracle SQLで、表1を表2で更新し、それを使用できるSQL更新クエリを実行するにはどうすればよいですか?だから私が得る最終結果はnamedescid

表1:

id    name    desc
-----------------------
1     x       123
2     y       345
3     c       adf

質問は、あるテーブルを別のテーブルのデータで更新することですが、特にOracle SQLの場合です。



他の質問に戻り、その回答を受け入れずに、Oracle PLSQL構文が必要であることを具体的に述べる必要があります。
p.campbell 2011

3
@ p.campbell、それは私の質問ではない...
Muhd

1
ああなるほど。したがって、質問の本文をコピーして貼り付けましたが、Oracleビットを含めるように変更しました。
p.campbell 2011

2
うん。「desc」は予約語なので、これはおそらく最良の例ではありませんが、まあ。
Muhd 2011

回答:


512

これは相関更新と呼ばれます

UPDATE table1 t1
   SET (name, desc) = (SELECT t2.name, t2.desc
                         FROM table2 t2
                        WHERE t1.id = t2.id)
 WHERE EXISTS (
    SELECT 1
      FROM table2 t2
     WHERE t1.id = t2.id )

結合によってキーが保持されるビューになると想定すると、次のこともできます。

UPDATE (SELECT t1.id, 
               t1.name name1,
               t1.desc desc1,
               t2.name name2,
               t2.desc desc2
          FROM table1 t1,
               table2 t2
         WHERE t1.id = t2.id)
   SET name1 = name2,
       desc1 = desc2

8
最初のコード例では、正しい結果を得るために外側のWHERE句が必要ですか?それとも、クエリを高速化するためだけに使用しますか?
Mathias Bader 2013

41
@totoro-最初の例では、に一致する行がない場合WHERE EXISTS、は行を更新できません。これがないと、のすべての行が更新され、に一致する行がない場合に値が設定されます。これは通常、発生させたいことではないため、通常は必要です。t1t2t1NULLt2WHERE EXISTS
ジャスティンケイブ

3
を追加すると、一意の行が生成されることに注意してSELECT ... FROM t2 ください。つまり、一意のキーを構成するすべてのフィールドを選択する必要があります。一意ではない主キーでは十分ではありません。一意性がない場合、@ PaulKarrのループのようなものに削減されます。一意の相関関係がない場合、各ソース行に対して複数のターゲット行が更新される可能性があります。
Andrew Leach、

2
:更新可能な結合のためのキー保存要件について説明asktom.oracle.com/pls/asktom/...を
ヴァジム・

1
@RachitSharma-これは、サブクエリ(からのクエリtable2)が1つ以上のtable1値に対して複数の行を返し、Oracleがどちらを使用するかを認識していないことを意味します。通常、これは、1つの異なる行を返すようにサブクエリを調整する必要があることを意味します。
Justin Cave

132

これを試して:

MERGE INTO table1 t1
USING
(
-- For more complicated queries you can use WITH clause here
SELECT * FROM table2
)t2
ON(t1.id = t2.id)
WHEN MATCHED THEN UPDATE SET
t1.name = t2.name,
t1.desc = t2.desc;

4
実際、非常に高速で、1159477行が15,5秒でマージされました
jefissu

3
2015年がこの回答に気付いてから、この質問にアクセスするすべての人に期待しています。これがまたあれば動作することに注意してくださいtable1table2同じテーブルです、ただの世話をON-partとWHEREのため-clause SELECTの-statement table2
sjngm

1
別のマージを行う必要があるたびに、インスピレーションを得るためにこの答えに戻ってきます。私はそれを印刷して壁に
額装

魅力的な作品!どうも!
davidwillianx

SELECT DISTINCT ID、FIELD1、FIELD1 FROM table2 WHERE ID NOT NOT NULL
Joseph Poirier

17

試す

UPDATE Table1 T1 SET
T1.name = (SELECT T2.name FROM Table2 T2 WHERE T2.id = T1.id),
T1.desc = (SELECT T2.desc FROM Table2 T2 WHERE T2.id = T1.id)
WHERE T1.id IN (SELECT T2.id FROM Table2 T2 WHERE T2.id = T1.id);

4
これの欠点は、SELECTステートメントが3回繰り返されることです。複雑な例では、それは取引ブレーカーになる可能性があります。
DavidBalažic17年

9
Update table set column = (select...)

setは1つの値しか期待しないため、私にとってはうまくいきませんでした-SQLエラー:ORA-01427:単一行のサブクエリが複数の行を返します。

これが解決策です:

BEGIN
For i in (select id, name, desc from table1) 
LOOP
Update table2 set name = i.name, desc = i.desc where id = i.id;
END LOOP;
END;

これが、SQLDeveloperワークシートで正確に実行する方法です。彼らはそれが遅いと言いますが、それがこの場合私のために働いた唯一の解決策です。


これが評判で-2に値する理由を誰かが説明できますか?笑。
Pau Karr 2013年

13
レートを下げませんでしたが、それは良い解決策ではありません。まず、副選択が複数の値を返していた場合、forループは、一部またはすべてのレコードに対して、table2の名前を複数回上書きします(クリーンではありません)。次に、order by句がないため、これは予測できない方法で発生します(つまり、順序付けされていないデータの最後の値が優先されます)。第三に、それははるかに遅くなります。意図していたため、ループの結果を想定すると、元副選択は、レコードごとに1つだけ値を返すために、いくつかの制御された方法で書き直されている可能性が...最も簡単な不自然な方法は、(...)分(名前を選択)だろう
オルタネータ

これはまさに私が必要としたものでした。ありがとう(+1)
ロバートハイアット

3
サブクエリで複数の値を取得する場合は、クエリを再考して、MIN、MAXを指定したDISTINCTまたはGROUP BYを使用できます。ただのアイデア。
フランシス

簡単に言えば、もしそれを避けることができるなら、決してT-SQLステートメントでいかなる種類のLOOPも決して使用しないでください。個人的には、他の解決策がない場合の時間の0.001%でなければ、T-SQLで使用可能な関数であるべきだとは思いません。T-SQLはセットベースになるように設計されているため、データセット全体に対して機能します。データを行ごとに処理するために使用しないでください。
レイK.

8

これは、結合に複数のキーを許可する「in」句を使用したさらに良い答えのようです:

update fp_active set STATE='E', 
   LAST_DATE_MAJ = sysdate where (client,code) in (select (client,code) from fp_detail
  where valid = 1) ...

ビーフは、「in」の前のwhere句で括弧としてキーとして使用する列を持ち、同じ列名が括弧で囲まれたselectステートメントを持っています。where(column1、column2)in(select(column1、column2)from table where "set I want");


リンクの有効期限が切れています。(404
ダンボ

-3

テーブルt1とそのバックアップt2に多くの列がある場合、これを行うコンパクトな方法を次に示します。

さらに、関連する問題として、一部の列のみが変更され、多くの行でこれらの列が編集されなかったため、それらをそのままにしておいた-基本的に、テーブル全体のバックアップから列のサブセットを復元した。すべての行を復元するだけの場合は、where句をスキップします。

もちろん、より簡単な方法は選択として削除して挿入することですが、私の場合、更新のみのソリューションが必要でした。

トリックは、重複する列名を持つテーブルのペアから*を選択すると、2番目のテーブルには_1という名前が付けられることです。だからここに私が思いついたものがあります:

  update (
    select * from t1 join t2 on t2.id = t1.id
    where id in (
      select id from (
        select id, col1, col2, ... from t2
        minus select id, col1, col2, ... from t1
      )
    )
  ) set col1=col1_1, col2=col2_1, ...

これは、Oracle 11gでは機能しません。この方法の実例を作成できますか?
ジョンヘラー2013年

-3
BEGIN
For i in (select id, name, desc from table2) 
LOOP
Update table1 set name = i.name, desc = i.desc where id = i.id and (name is null or desc is null);
END LOOP;
END;
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.