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 ฉันจะเรียกใช้คิวรีการอัปเดต sqlที่สามารถอัปเดตตารางที่ 1 ด้วยตารางที่ 2 nameและdescใช้งานเดียวกันได้idอย่างไร ดังนั้นผลลัพธ์สุดท้ายที่ฉันจะได้รับคือ

ตารางที่ 1:

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

คำถามมาจากการอัพเดตหนึ่งตารางด้วยข้อมูลจากอีกตารางหนึ่งแต่เฉพาะสำหรับ oracle SQL


2
อาจเป็นไปได้ที่ซ้ำกันของ
คิวรี

คุณต้องย้อนกลับไปที่คำถามอื่นยกเลิกคำตอบนั้นและระบุอย่างเจาะจงว่าคุณต้องการไวยากรณ์ Oracle PLSQL
p.campbell

3
@ p.campbell นั่นคือไม่ได้เป็นคำถามของฉัน ...
Muhd

1
อ้อเข้าใจแล้ว. ดังนั้นคุณจึงคัดลอกเนื้อความคำถาม แต่ปรับเปลี่ยนเพื่อรวมบิตของ Oracle
p.campbell

2
ใช่. และนี่อาจไม่ใช่ตัวอย่างที่ดีที่สุดเนื่องจาก "desc" เป็นคำที่สงวนไว้ แต่ก็ดี
Muhd

คำตอบ:


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-clause จำเป็นสำหรับผลลัพธ์ที่ถูกต้องหรือไม่? หรือคุณใช้เพื่อเพิ่มความเร็วในการสืบค้นเท่านั้น?
งัด Bader

41
@totoro - ในตัวอย่างแรกที่WHERE EXISTSป้องกันคุณจากการปรับปรุงแถวในถ้าไม่มีแถวที่ตรงกันในt1 t2โดยไม่ได้ทุกคนในแถวt1จะมีการปรับปรุงและค่าที่จะถูกตั้งค่าถ้าไม่มีแถวที่ตรงกันในNULL t2โดยทั่วไปไม่ใช่สิ่งที่คุณต้องการให้เกิดขึ้นดังนั้นจึงWHERE EXISTSเป็นสิ่งจำเป็นโดยทั่วไป
Justin Cave

3
มันมีค่าเพิ่มที่SELECT ... FROM t2 จะต้องส่งผลให้แถวที่ไม่ซ้ำกัน ซึ่งหมายความว่าคุณต้องเลือกในทุกฟิลด์ซึ่งประกอบด้วยคีย์ที่ไม่ซ้ำกัน - คีย์หลักที่ไม่ซ้ำกันไม่เพียงพอ หากไม่มีเอกลักษณ์คุณจะลดการวนซ้ำของ @ PaulKarr - และหากไม่มีความสัมพันธ์ที่ไม่ซ้ำกันอาจมีการอัพเดตแถวเป้าหมายมากกว่าหนึ่งแถวสำหรับแต่ละแถวต้นฉบับ
Andrew Leach

2
คำอธิบายเกี่ยวกับข้อกำหนดที่สงวนไว้สำหรับการเข้าร่วมที่อัปเดตได้: asktom.oracle.com/pls/asktom/ ......
Vadzim

1
@RachitSharma - นั่นหมายถึงแบบสอบถามย่อยของคุณ (แบบสอบถามจากtable2) ส่งคืนแถวหลายแถวสำหรับค่าอย่างน้อยหนึ่งtable1ค่าและ Oracle ไม่ทราบว่าคุณต้องการใช้แถวใด โดยปกตินั่นหมายความว่าคุณจำเป็นต้องปรับแต่งแบบสอบถามย่อยเพื่อที่จะส่งกลับแถวที่แตกต่างกันเพียงครั้งเดียว
Justin Cave

133

ลองสิ่งนี้:

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,5s
jefissu

4
ฉันหวังว่าทุกคนที่เยี่ยมชมคำถามนี้หลังจากปี 2015 สังเกตคำตอบนี้ ทราบว่านี้ยังทำงานถ้าtable1และtable2เป็นตารางเดียวกันเพียง แต่ดูแลของON-part และWHERE-clause สำหรับSELECT-statement ของtable2!
sjngm

1
ฉันพบว่าทุกครั้งที่ฉันต้องทำการผสานอีกครั้งฉันจะกลับมาที่คำตอบนี้เพื่อหาแรงบันดาลใจ ฉันอาจพิมพ์มันออกมาและวางกรอบลงบนผนังของฉัน
arnehehe

ทำงานเหมือนจับใจ !! ขอบคุณ!
davidwillianx

เลือก DISTINCT ID, FIELD1, FIELD1 จาก table2 WHERE ID ไม่ว่าง
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 ครั้ง ในตัวอย่างที่ซับซ้อนที่สามารถจัดการได้
David Balažic

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

ไม่เคยทำงานให้ฉันตั้งแต่ตั้งค่าเพียง 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

13
ฉันไม่ได้ให้คะแนน แต่มันไม่ใช่ทางออกที่ดี ประการแรก: หากการเลือกย่อยส่งคืนค่าหลายค่าดังนั้น for for จะเขียนทับชื่อบน table2 หลาย ๆ ครั้งสำหรับบางระเบียน / ทั้งหมด (ไม่สะอาด) ประการที่สอง: ไม่มีคำสั่งซื้อตามข้อดังนั้นสิ่งนี้จะเกิดขึ้นในลักษณะที่ไม่สามารถคาดเดาได้ (เช่นค่าสุดท้ายในข้อมูลที่ไม่ได้เรียงลำดับชนะ) ประการที่สาม: มันจะช้ากว่ามาก สมมติว่าผลลัพธ์ของ for for loop นั้นได้มีการสร้าง subselect ดั้งเดิมขึ้นใหม่ในบางวิธีที่ควบคุมเพื่อส่งกลับเพียง 1 ค่าสำหรับแต่ละระเบียน ... วิธีการที่ง่ายที่สุดจะเป็น (เลือก min (ชื่อ) ... )
Alternator

นี่คือสิ่งที่ฉันต้องการ ขอบคุณ (+1)
Robert Hyatt

3
หากคุณได้รับหลายค่าในแบบสอบถามย่อยของคุณคุณอาจคิดใหม่แบบสอบถามและใช้ DISTINCT หรือ GROUP BY ด้วย MIN, MAX แค่ความคิด
ฟรานซิส

สั้นเรื่องสั้น: ถ้าคุณสามารถหลีกเลี่ยงได้ไม่เคยใช้ LOOP ชนิดใด ๆ ในคำสั่ง T-SQL โดยส่วนตัวถ้าไม่ใช่ 0.001% ของเวลาที่ไม่มีวิธีแก้ปัญหาอื่น ๆ ฉันไม่คิดว่ามันจะเป็นฟังก์ชั่นที่มีอยู่ใน T-SQL T-SQL ได้รับการออกแบบให้ตั้งค่าตามมาตรฐานดังนั้นจึงสามารถทำงานกับชุดข้อมูลทั้งหมดได้ ไม่ควรใช้กับ data line-by-line
Ray K.

8

ที่นี่ดูเหมือนจะเป็นคำตอบที่ดียิ่งขึ้นด้วยประโยค 'ใน' ที่อนุญาตให้ใช้หลายปุ่มสำหรับการเข้าร่วม :

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

เนื้อวัวอยู่ในการมีคอลัมน์ที่คุณต้องการใช้เป็นกุญแจสำคัญในวงเล็บในที่ข้อก่อน 'ใน' และมีคำสั่งเลือกที่มีชื่อคอลัมน์เดียวกันในวงเล็บ โดยที่ ( คอลัมน์ 1, คอลัมน์ 2 ) ใน ( เลือก ( คอลัมน์ 1, คอลัมน์ 2 ) จากตารางที่ "ชุดที่ฉันต้องการ" );


ลิงก์หมดอายุ ( 404)
Dumbo

-3

หากตาราง t1 ของคุณและเป็นแบ็คอัพ t2 มีหลายคอลัมน์นี่เป็นวิธีกระชับข้อมูล

นอกจากนี้ปัญหาที่เกี่ยวข้องของฉันคือมีเพียงคอลัมน์บางคอลัมน์เท่านั้นที่ถูกแก้ไขและหลายแถวไม่มีการแก้ไขคอลัมน์เหล่านี้ดังนั้นฉันต้องการปล่อยให้คอลัมน์เหล่านั้นอยู่คนเดียว - โดยทั่วไปจะเรียกคืนชุดย่อยของคอลัมน์จากการสำรองข้อมูลของทั้งตาราง หากคุณต้องการกู้คืนแถวทั้งหมดให้ข้ามส่วนคำสั่งที่

แน่นอนวิธีที่ง่ายกว่าคือการลบและใส่เป็นการเลือก แต่ในกรณีของฉันฉันต้องการโซลูชันที่มีการอัปเดต

เคล็ดลับคือเมื่อคุณเลือก * จากคู่ของตารางที่มีชื่อคอลัมน์ซ้ำกันอันดับที่ 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 คุณสามารถสร้างตัวอย่างการทำงานของวิธีนี้ได้ไหม
Jon Heller

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.