Klausa ekspresi tabel DML hanya berguna saat Anda membutuhkan kolom dari lebih dari satu tabel. Dalam kasus Anda, Anda dapat menggunakan pembaruan rutin dengan EXISTS
:
update web_userrole
set role = replace(role, 'FULL', 'READ')
where read_only <> 'Y'
and exists
(
select 1/0
from web_userdatasource
where datasource = p_datasource
and username = web_userrole.username
);
Jika Anda benar-benar perlu menggunakan kolom dari kedua tabel, Anda memiliki tiga opsi:
- ulangi join di
SET
danWHERE
ayat. Ini mudah dibuat tetapi tidak optimal. - Ekspresi tabel DML. Ini harus bekerja, jika Anda memiliki indeks yang benar.
-
MERGE
, di bawah ini adalah contohnya.merge into web_userrole using ( select distinct username from web_userdatasource where datasource = p_datasource ) web_userdatasource on ( web_userrole.username = web_userdatasource.username and web_userrole.read_only <> 'Y' ) when matched then update set role = replace(role, 'FULL', 'READ');
Ini tidak secara langsung menjawab pertanyaan Anda, tetapi memberikan beberapa solusi. Saya tidak dapat mereproduksi kesalahan yang Anda dapatkan. Saya memerlukan kasus uji lengkap untuk memeriksanya lebih lanjut.
Saran umum untuk tampilan yang dapat diperbarui
Salah satu masalah utama dengan tampilan yang dapat diperbarui adalah banyaknya pembatasan pada kueri yang dapat dikandungnya. Kueri atau tampilan tidak boleh berisi banyak fitur, seperti DISTINCT, GROUP BY, ekspresi tertentu, dll. Kueri dengan fitur tersebut dapat memunculkan pengecualian "ORA-01732:operasi manipulasi data tidak sah pada tampilan ini".
Kueri tampilan yang dapat diperbarui harus secara jelas mengembalikan setiap baris tabel yang dimodifikasi hanya satu kali. Kueri harus “dipertahankan kunci”, yang berarti Oracle harus dapat menggunakan kunci utama atau batasan unik untuk memastikan bahwa setiap baris hanya diubah satu kali.
Untuk mendemonstrasikan mengapa kunci yang diawetkan itu penting, kode di bawah ini membuat pernyataan pembaruan yang ambigu. Ini menciptakan dua tabel, tabel pertama memiliki satu baris dan tabel kedua memiliki dua baris. Tabel bergabung dengan kolom A
, dan coba perbarui kolom B
di tabel pertama. Dalam hal ini ada baiknya Oracle mencegah pembaruan, jika tidak, nilainya akan menjadi non-deterministik. Terkadang nilainya disetel ke "1", terkadang disetel ke "2".
--Create table to update, with one row.
create table test1 as
select 1 a, 1 b from dual;
--Create table to join two, with two rows that match the other table's one row.
create table test2 as
select 1 a, 1 b from dual union all
select 1 a, 2 b from dual;
--Simple view that joins the two tables.
create or replace view test_view as
select test1.a, test1.b b_1, test2.b b_2
from test1
join test2 on test1.a = test2.a;
--Note how there's one value of B_1, but two values for B_2.
select *
from test_view;
A B_1 B_2
- --- ---
1 1 1
1 1 2
--If we try to update the view it fails with this error:
--ORA-01779: cannot modify a column which maps to a non key-preserved table
update test_view
set b_1 = b_2;
--Using a subquery also fails with the same error.
update
(
select test1.a, test1.b b_1, test2.b b_2
from test1
join test2 on test1.a = test2.a
)
set b_1 = b_2;
MERGE
pernyataan tidak memiliki batasan yang sama. MERGE
pernyataan tampaknya mencoba mendeteksi ambiguitas pada waktu proses, bukan waktu kompilasi.
Sayangnya MERGE
tidak selalu melakukan pekerjaan yang baik untuk mendeteksi ambiguitas. Pada Oracle 12.2, pernyataan di bawah ini kadang-kadang akan berfungsi, dan kemudian gagal. Membuat perubahan kecil pada kueri dapat membuatnya berfungsi atau gagal, tetapi saya tidak dapat menemukan pola tertentu.
--The equivalent MERGE may work and changes "2" rows, even though there's only one.
--But if you re-run, or uncomment out the "order by 2 desc" it might raise:
-- ORA-30926: unable to get a stable set of rows in the source tables
merge into test1
using
(
select test1.a, test1.b b_1, test2.b b_2
from test1
join test2 on test1.a = test2.a
--order by 2 desc
) new_rows
on (test1.a = new_rows.a)
when matched then update set test1.b = new_rows.b_2;
UPDATE
gagal pada waktu kompilasi jika secara teoritis memungkinkan untuk memiliki duplikat. Beberapa pernyataan yang seharusnya pekerjaan tidak akan berjalan.
MERGE
gagal jika database mendeteksi baris yang tidak stabil saat dijalankan. Beberapa pernyataan yang tidak boleh pekerjaan akan tetap berjalan.