Perbarui pernyataan dengan gabung dalam pada Oracle

298

Saya memiliki permintaan yang berfungsi baik di MySQL, tetapi ketika saya menjalankannya di Oracle saya mendapatkan kesalahan berikut:

Kesalahan SQL: ORA-00933: Perintah SQL tidak berakhir dengan benar
00933. 00000 - "Perintah SQL tidak berakhir dengan benar"

Pertanyaannya adalah:

UPDATE table1
INNER JOIN table2 ON table1.value = table2.DESC
SET table1.value = table2.CODE
WHERE table1.UPDATETYPE='blah';
pengguna169743
sumber
Ketika saya mencoba men-setup table2 di Oracle untuk menguji jawaban saya, saya menemukan bahwa Oracle menolak DESC sebagai nama kolom.
Janek Bogucki
Maaf saya baru saja menyingkat nama kolom asli untuk mendeskripsikannya jelas bukan di db
user169743

Jawaban:

412

Sintaks itu tidak valid di Oracle. Kamu bisa melakukan ini:

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

Atau Anda mungkin dapat melakukan ini:

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

Itu tergantung jika tampilan inline dianggap dapat diperbarui oleh Oracle ( Untuk dapat diperbarui untuk pernyataan kedua tergantung pada beberapa aturan yang tercantum di sini ).

Tony Andrews
sumber
5
Saya melakukan contoh kedua tetapi harus menambahkan alias ke nama kolom di pilih dan kemudian merujuk mereka dengan nama mereka di SET tetapi berhasil, terima kasih
Gustavo Rubio
41
Contoh kedua memiliki manfaat memungkinkan Anda untuk menguji SQL sebelum benar-benar melakukan pembaruan.
Daniel Reis
10
Contoh kedua bekerja untuk saya. Saya suka yang itu karena terlihat bersih dan mudah dibaca. Tidak tahu apa pro dan kontra di antara keduanya ketika datang ke kinerja. Tapi, saya tidak khawatir tentang itu untuk saat ini karena saya menggunakan ini untuk skrip satu untuk memperbaiki data yang buruk.
nemo
5
Kedua bekerja untuk saya :). Oracle adalah salah satu hewan yang kuat tapi aneh: /
elrado
10
Penjelasan mengenai persyaratan terpelihara kunci untuk gabungan yang
Vadzim
202

Gunakan ini:

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;
Quassnoi
sumber
2
Bekerja dengan sempurna tetapi Oracle meminta saya untuk mengatakan merge into table 1 tdan sebagainya.
Michael-O
1
Terlambat ke pesta, tapi ini masih utas yang bagus. Saya perlu tahu, apakah ... apakah saya melewatkan sesuatu? Tabel master, "table1". Dalam USING, table1 alias t1. Table2, alias t2, tetapi dalam ON, referensi adalah ...? Table1 Eksternal - bukan t1 - apakah ini referensi ke tabel luar atau tipe? Meja 2? Bukan t2? Je suis bingung. Penggemar alias lebih baik ...
Marc
Tepat di sini, jika kunci Anda (trg.rowid atau src.rid) memiliki satu item duplikat, klausa ini membuat kesalahan: ora-30926.ora-code.com
Henrique
@ Mark Dalam ON, trgadalah alias untuk tabel master, table1("luar" tabel oleh logika Anda), dan srcreferensi USINGgrup ("tabel dalam" oleh logika Anda). Tapi ya, mungkin bisa dirujuk lebih baik, tapi saya bisa mengikutinya.
vapcguy
1
@supernova: jawaban tony memperbarui tampilan inline. Ini dapat berfungsi dalam beberapa kasus, tetapi tampilan harus "dipelihara dengan kunci" (setiap tabel yang digabung harus digabung dengan kesetaraan pada kunci primer atau bidang unik). Ini memastikan setiap record dalam tabel target berkontribusi paling banyak satu record dalam rowset yang dihasilkan, dan, karenanya, setiap record dalam tabel target diperbarui paling banyak satu kali.
Quassnoi
25

MERGEdengan WHEREklausa:

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;

Anda memerlukan WHEREklausa karena kolom yang dirujuk dalam ONklausa tidak dapat diperbarui.

Roland
sumber
Versi ini bisa dibilang lebih bersih, tetapi tidak memicu ramah karena tidak ada cara saya menyadari untuk menghindari memicu pemicu pembaruan untuk baris tidak berubah menggunakan sintaks ini. (Saya berasumsi bahwa pemicu diperlukan untuk baris yang diubah .)
sf_jeff
14
 UPDATE ( SELECT t1.value, t2.CODE
          FROM table1 t1
          INNER JOIN table2 t2 ON t1.Value = t2.DESC
          WHERE t1.UPDATETYPE='blah')
 SET t1.Value= t2.CODE
Morten Anderson
sumber
11

Jangan gunakan beberapa jawaban di atas.

Beberapa menyarankan penggunaan SELECT bersarang, jangan lakukan itu, itu sangat lambat. Jika Anda memiliki banyak catatan untuk diperbarui, gunakan bergabung, jadi sesuatu seperti:

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;

Lihat tautan ini untuk lebih jelasnya. http://geekswithblogs.net/WillSmith/archive/2008/06/18/oracle-update-with-join-again.aspx .

Juga, pastikan bahwa ada kunci utama pada semua tabel yang Anda gabungkan.

duvo
sumber
7

Seperti yang ditunjukkan di sini , sintaks umum untuk solusi pertama yang diusulkan oleh Tony Andrews adalah:

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
                         )

Saya pikir ini menarik terutama jika Anda ingin memperbarui lebih dari satu bidang.

Alexis Dufrenoy
sumber
Ini tidak berhasil untuk saya. Ini memperbarui seluruh tabel.
Natassia Tavares
3

Sintaks berikut ini berfungsi untuk saya.

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;
Hemant
sumber
@JimGarrison Harap edit kembali jawaban ini sehingga saya dapat menghapus downvote saya .... Saya mencoba menggunakan sintaks ini dan tidak memperbarui tabel saya. Saya mengetahui mengapa - saya SETsedang melakukan REPLACEdan saya mencoba mengosongkan string tertentu di kolom - ternyata Oracle memperlakukan ''sebagai null, dan bidang ini tidak dapat dibatalkan. Saya pikir sintaks hanya memperbarui tabel temp bukan yang asli, tapi saya salah.
vapcguy
2

Menggunakan deskripsi alih-alih desc untuk 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'
;
Janek Bogucki
sumber
mengapa Anda ingin memecat dua pertanyaan terpisah di table2
Jitendra Vispute
2

Ini bekerja dengan baik oracle

merge into table1 t1
using (select * from table2) t2
on (t1.empid = t2.empid)
when matched then update set t1.salary = t2.salary
pengguna5299305
sumber
Dapat mengatur beberapa properti dengan menambahkan koma di akhir itu. Saya perlu melakukan t1.First_Name = t2.FirstName, t1.Last_Name = t2.LastNamepada tabel setelah mencocokkannya di kolom "UserName" ( t1.UserName = t2.UserName) untuk mengambil nama mereka dari tabel yang disebut UserInfo ( select * from UserInfo) t2). Basis data sedemikian rupa di mana ia menggunakan UserName sebagai kunci utama untuk UserInfo di mana-mana, alih-alih menempatkan FirstName dan LastName dalam tabel, secara langsung. Ini memperbaikinya!
vapcguy
Jawaban ini tidak menambahkan apa pun ke jawaban yang sudah diberikan oleh Quassnoi lima tahun sebelum jawaban Anda.
Hijauan
0
UPDATE table1 t1
SET t1.value = 
    (select t2.CODE from table2 t2 
     where t1.value = t2.DESC) 
WHERE t1.UPDATETYPE='blah';
afnhsn
sumber
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`
pengguna5685973
sumber
0

Sama seperti kelengkapan, dan karena kita berbicara Oracle, ini bisa melakukannya juga:

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;
/
Edu Castrillon
sumber
1
Ini bisa melakukannya, tapi ini tentang cara paling lambat yang mungkin.
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 dan B adalah bidang alias, Anda tidak perlu mengarahkan tabel.

Dan Anderson
sumber
1
Hai Dan. Anda memposting ke pertanyaan yang cukup lama yang sudah memiliki jawaban yang sangat bagus. Bisakah Anda menjelaskan ketika pertanyaan Anda lebih disukai daripada solusi lain?
Noel Widmer
1
Tentu saja, saya telah melihat jawaban di mana b = a ditulis dengan menunjuk nama tabel (table1.B = table2.A) tetapi tidak perlu menunjukkan tabel.
Dan Anderson
Anda benar-benar memperbarui bidang dari tampilan, yang dipetakan ke tabel. Jika pandangan bagian dalam adalah alias h, maka versi "mendokumentasikan diri" akan menjadi "set hb = ha".
sf_jeff
-4
update table1  a 
   set a.col1='Y' 
 where exists(select 1 
                from table2 b
               where a.col1=b.col1 
                 and a.col2=b.col2
             )
uji
sumber