อัปเดตคำชี้แจงเมื่อมีการเข้าร่วมภายในกับ Oracle


298

ฉันมีคำถามซึ่งทำงานได้ดีใน MySQL แต่เมื่อฉันเรียกใช้บน Oracle ฉันได้รับข้อผิดพลาดต่อไปนี้:

ข้อผิดพลาด SQL: ORA-00933: คำสั่ง SQL ไม่สิ้นสุดอย่างถูกต้อง
00933 00000 - "คำสั่ง SQL ไม่สิ้นสุดอย่างถูกต้อง"

แบบสอบถามคือ:

UPDATE table1
INNER JOIN table2 ON table1.value = table2.DESC
SET table1.value = table2.CODE
WHERE table1.UPDATETYPE='blah';

เมื่อฉันพยายามตั้งค่า table2 ใน Oracle เพื่อทดสอบคำตอบของฉันฉันพบว่า Oracle ปฏิเสธ DESC เป็นชื่อคอลัมน์
Janek Bogucki

ขออภัยฉันเพิ่งย่อชื่อคอลัมน์เดิมเพื่อให้ชัดเจนว่าไม่ใช่ใน db
user169743

คำตอบ:


412

ไวยากรณ์นั้นไม่ถูกต้องใน Oracle คุณสามารถทำได้:

UPDATE table1 SET table1.value = (SELECT table2.CODE
                                  FROM table2 
                                  WHERE table1.value = table2.DESC)
WHERE table1.UPDATETYPE='blah'
AND EXISTS (SELECT table2.CODE
            FROM table2 
            WHERE table1.value = table2.DESC);

หรือคุณอาจทำสิ่งนี้ได้:

UPDATE 
(SELECT table1.value as OLD, table2.CODE as NEW
 FROM table1
 INNER JOIN table2
 ON table1.value = table2.DESC
 WHERE table1.UPDATETYPE='blah'
) t
SET t.OLD = t.NEW

ขึ้นอยู่กับว่า Oracle view สามารถอัปเดตมุมมองแบบอินไลน์ได้หรือไม่ ( เพื่อให้สามารถอัพเดตได้สำหรับคำสั่งที่สองขึ้นอยู่กับกฎบางอย่างที่แสดงไว้ ที่นี่ )


5
ฉันทำตัวอย่างที่สอง แต่ต้องเพิ่มชื่อแทนในชื่อคอลัมน์ในตัวเลือกแล้วอ้างอิงพวกเขาด้วยชื่อในตลาดหลักทรัพย์ แต่ก็ใช้งานได้ขอบคุณ
Gustavo Rubio

41
ตัวอย่างที่สองมีประโยชน์ในการอนุญาตให้คุณทดสอบ SQL ก่อนที่จะทำการอัพเดตจริง
Daniel Reis

10
ตัวอย่างที่สองเหมาะกับฉัน ฉันชอบอันนั้นเพราะมันดูสะอาดและอ่านง่าย ไม่รู้ว่าอะไรคือข้อดีและข้อเสียระหว่างสองสิ่งนี้เกี่ยวกับประสิทธิภาพ แต่ฉันไม่ได้กังวลเกี่ยวกับสิ่งนั้นในตอนนี้เพราะฉันใช้สิ่งนี้เป็นหนึ่งในสคริปต์เพื่อแก้ไขข้อมูลไม่ดี
nemo

5
ที่สองทำงานให้ฉัน :) Oracle เป็นสัตว์ที่แข็งแกร่ง แต่แปลกประหลาด: /
elrado

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

202

ใช้สิ่งนี้:

MERGE
INTO    table1 trg
USING   (
        SELECT  t1.rowid AS rid, t2.code
        FROM    table1 t1
        JOIN    table2 t2
        ON      table1.value = table2.DESC
        WHERE   table1.UPDATETYPE='blah'
        ) src
ON      (trg.rowid = src.rid)
WHEN MATCHED THEN UPDATE
    SET trg.value = code;

2
ทำงานได้อย่างสมบูรณ์แบบ แต่ Oracle ต้องการให้ฉันพูดmerge into table 1 tและอื่น ๆ
Michael-O

1
ไปงานปาร์ตี้สาย แต่นี่ก็ยังคงเป็นกระทู้ที่ดี ฉันต้องรู้ก็เถอะ ... ฉันคิดถึงอะไรบางอย่างเหรอ? ตารางหลัก "table1" ในการใช้งาน table1 ใช้นามแฝงเป็น t1 Table2, aliased เป็น t2 แต่ใน ON การอ้างอิงคือ ... ? ตารางภายนอก 1 - ไม่ใช่ t1 - นี่คือการอ้างอิงถึงตารางด้านนอกหรือประเภท? Table2? ไม่ใช่ t2 ใช่ไหม Je suis สับสน แฟนของชื่อแทนดีกว่า ...
มาร์ค

เพียงจุดเดียวที่นี่หากคีย์ของคุณ (trg.rowid หรือ src.rid) มีรายการที่ซ้ำกันข้อนี้มีข้อผิดพลาด: ora-30926.ora-code.com
Henrique

@Marc ในON, trgเป็นนามแฝงสำหรับตารางต้นแบบ, table1("ตารางด้านนอก" โดยตรรกะของคุณ), และsrcอ้างอิงUSINGกลุ่ม ("ตารางภายใน" ด้วยตรรกะของคุณ) แต่ใช่อาจถูกอ้างอิงได้ดีกว่า แต่ฉันก็สามารถติดตามได้
vapcguy

1
@supernova: คำตอบของ tony กำลังอัปเดตมุมมองอินไลน์ สิ่งนี้สามารถใช้งานได้ในบางกรณี แต่มุมมองจะต้องเป็น "การเก็บรักษาคีย์" (ทุกตารางที่รวมจะต้องมีการเข้าร่วมเท่ากันบนคีย์หลักหรือชุดข้อมูลที่ไม่ซ้ำกัน) สิ่งนี้ทำให้แน่ใจว่าทุกเร็กคอร์ดในตารางเป้าหมายมีส่วนอย่างมากที่สุดหนึ่งเร็กคอร์ดในชุดผลลัพธ์ที่ได้และด้วยเหตุนี้ทุกเร็กคอร์ดในตารางเป้าหมายจะถูกอัพเดตอย่างมากที่สุดหนึ่งครั้ง
Quassnoi

25

MERGEด้วยWHEREข้อ:

MERGE into table1
USING table2
ON (table1.id = table2.id)
WHEN MATCHED THEN UPDATE SET table1.startdate = table2.start_date
WHERE table1.startdate > table2.start_date;

คุณต้องการส่วนWHEREคำสั่งเนื่องจากคอลัมน์ที่อ้างถึงในส่วนONคำสั่งไม่สามารถอัปเดตได้


รุ่นนี้มีเนื้อหาที่สะอาดกว่า แต่มันไม่เปิดใช้งานได้ง่ายเนื่องจากไม่มีวิธีที่ฉันทราบเพื่อหลีกเลี่ยงการทริกเกอร์การปรับปรุงทริกเกอร์สำหรับแถวที่ไม่มีการเปลี่ยนแปลงโดยใช้ไวยากรณ์นี้ (ฉันสมมติว่าทริกเกอร์เป็นสิ่งจำเป็นสำหรับแถวที่เปลี่ยนแปลง )
357


11

อย่าใช้คำตอบบางอย่างข้างต้น

บางคนแนะนำให้ใช้ SELECT ซ้อนกันอย่าทำอย่างนั้นมันช้าเลือดตาแทบกระเด็น หากคุณมีบันทึกจำนวนมากที่จะอัปเดตให้ใช้การเข้าร่วมดังนั้น:

update (select bonus 
        from employee_bonus b 
        inner join employees e on b.employee_id = e.employee_id 
        where e.bonus_eligible = 'N') t
set t.bonus = 0;

ดูลิงค์นี้สำหรับรายละเอียดเพิ่มเติม http://geekswithblogs.net/WillSmith/archive/2008/06/18/oracle-update-with-join-again.aspx

ตรวจสอบให้แน่ใจว่ามีคีย์หลักในตารางทั้งหมดที่คุณเข้าร่วม


7

ตามที่ระบุไว้ที่นี่ไวยากรณ์ทั่วไปสำหรับโซลูชั่นแรกที่เสนอโดย Tony Andrews คือ:

update some_table s
set   (s.col1, s.col2) = (select x.col1, x.col2
                          from   other_table x
                          where  x.key_value = s.key_value
                         )
where exists             (select 1
                          from   other_table x
                          where  x.key_value = s.key_value
                         )

ฉันคิดว่านี่น่าสนใจโดยเฉพาะถ้าคุณต้องการอัปเดตมากกว่าหนึ่งช่อง


มันไม่ได้ผลสำหรับฉัน ปรับปรุงตารางทั้งหมด
Natassia Tavares

3

ไวยากรณ์ต่อไปนี้ใช้งานได้สำหรับฉัน

UPDATE
(SELECT A.utl_id,
    b.utl1_id
    FROM trb_pi_joint A
    JOIN trb_tpr B
    ON A.tp_id=B.tp_id Where A.pij_type=2 and a.utl_id is null
)
SET utl_id=utl1_id;

@JimGarrison โปรดแก้ไขคำตอบนี้อีกครั้งเพื่อให้ฉันสามารถลบ downvote ของฉันได้ .... ฉันพยายามใช้ไวยากรณ์นี้และไม่ได้อัปเดตตารางของฉัน ฉันพบว่าทำไม - ฉันSETกำลังทำREPLACEและฉันพยายามที่จะทำให้สตริงว่างในคอลัมน์ - ปรากฎว่า Oracle ถือว่า''เป็นโมฆะและฟิลด์นี้ไม่สามารถเป็นโมฆะ ฉันคิดว่าไวยากรณ์เพิ่งปรับปรุงตารางชั่วคราวแทนที่จะเป็นของจริง แต่ฉันคิดผิด
vapcguy

2

ใช้คำอธิบายแทนเรียงสำหรับ table2

update
  table1
set
  value = (select code from table2 where description = table1.value)
where
  exists (select 1 from table2 where description = table1.value)
  and
  table1.updatetype = 'blah'
;

เหตุใดคุณจึงต้องการเรียกใช้แบบสอบถามสองชุดแยกกันใน table2
Jitendra Vispute

2

มันทำงานได้ดี oracle

merge into table1 t1
using (select * from table2) t2
on (t1.empid = t2.empid)
when matched then update set t1.salary = t2.salary

สามารถตั้งค่าคุณสมบัติหลายรายการได้โดยเพิ่มเครื่องหมายจุลภาคที่ส่วนท้าย ฉันต้องทำt1.First_Name = t2.FirstName, t1.Last_Name = t2.LastNameในตารางหลังจากจับคู่ในคอลัมน์ "ชื่อผู้ใช้" ( t1.UserName = t2.UserName) เพื่อดึงชื่อจากตารางที่เรียกว่า UserInfo ( select * from UserInfo) t2) ฐานข้อมูลเป็นแบบที่มันใช้ชื่อผู้ใช้เป็นคีย์หลักของ UserInfo ทุกหนทุกแห่งแทนที่จะวาง FirstName และ LastName ในตารางโดยตรง นี้คงที่!
vapcguy

คำตอบนี้ไม่ได้เพิ่มคำตอบใด ๆ ที่ Quassnoi จัดหาให้ก่อนหน้าคุณห้าปี
อาหาร


0
UPDATE IP_ADMISSION_REQUEST ip1
SET IP1.WRIST_BAND_PRINT_STATUS=0
WHERE IP1.IP_ADM_REQ_ID        =
  (SELECT IP.IP_ADM_REQ_ID
  FROM IP_ADMISSION_REQUEST ip
  INNER JOIN VISIT v
  ON ip.ip_visit_id=v.visit_id
  AND v.pat_id     =3702
  ); `enter code here`

0

เช่นเดียวกับความสมบูรณ์และเนื่องจากเรากำลังพูดถึง Oracle สิ่งนี้สามารถทำได้เช่นกัน:

declare
begin
  for sel in (
    select table2.code, table2.desc
    from table1
    join table2 on table1.value = table2.desc
    where table1.updatetype = 'blah'
  ) loop
    update table1 
    set table1.value = sel.code
    where table1.updatetype = 'blah' and table1.value = sel.desc;    
  end loop;
end;
/

1
สิ่งนี้สามารถทำได้ แต่มันเกี่ยวกับวิธีที่ช้าที่สุดที่เป็นไปได้
APC

-1
UPDATE (SELECT T.FIELD A, S.FIELD B
FROM TABLE_T T INNER JOIN TABLE_S S
ON T.ID = S.ID)
SET B = A;

A และ B เป็นเขตข้อมูลนามแฝงคุณไม่จำเป็นต้องชี้ตาราง


1
สวัสดีแดน คุณกำลังโพสต์คำถามเก่า ๆ ที่มีคำตอบที่ดีอยู่แล้ว คุณช่วยอธิบายได้ไหมเมื่อคำถามของคุณดีกว่าโซลูชันอื่น ๆ
Noel Widmer

1
แน่นอนฉันเห็นคำตอบโดยที่ b = a เขียนโดยชี้ชื่อตาราง (table1.B = table2.A) แต่ไม่จำเป็นต้องชี้ตาราง
Dan Anderson

คุณกำลังอัปเดตเขตข้อมูลจากมุมมองซึ่งได้รับการแมปกับตาราง หากมุมมองภายในเป็น aliased h ดังนั้นเวอร์ชัน "การบันทึกเอกสารด้วยตนเอง" จะเป็น "set hb = ha"
sf_jeff

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