關於ORA-01779問題的分析和解決
最近同事問我一個問題,是關於一個update語句的問題,需求有點特別,結果在使用update語句嘗試了各種方法後,仍然是不依不饒的報出ORA-01779的錯誤。今天專門花時間分析了一下這個問題,還是有些收穫。
為了說明問題,我們先來看兩組資料
有兩個表data,link,現在希望更新data表中的amount列值,和link表的credit_class做關聯。
如果data.credit_class=link.credit_class,就直接把data.amount修改為link.score。
邏輯的難點是,如果data.credit_class!=link.credit_class就把data.amount修改為link.credit_class='*'對應的score值
以下面的資料為例,
對於id為1的資料來說,data.credit_class在link.credit_class中存在,所以就需要把amount從110修改為100
對於id未9的資料來說,data.credit_class在link.credit_class中不匹配,所以就找到link.credit_class='*'的值1,然後把data.amount的值從9修改為1
SQL> select *from data;
ID CREDIT_CLASS AMOUNT
---------- ------------------------------ ----------
1 vip 110
2 vip 110
3 vip 110
4 vip 110
5 vip 110
6 vip 110
7 vip 110
8 vip 110
9 normal 9
10 normal 9
10 rows selected.
SQL> select *from link;
CREDIT_CLASS SCORE
------------------------------ ----------
vip 100
agent 10
* 1
關於關聯update的問題,比較經典的錯誤就是
ERROR at line 11:
ORA-01779: cannot modify a column which maps to a non key-preserved table
問題的解釋如下:
SQL> !oerr ora 01779
01779, 00000, "cannot modify a column which maps to a non key-preserved table"
// *Cause: An attempt was made to insert or update columns of a join view which
// map to a non-key-preserved table.
// *Action: Modify the underlying base tables directly.
為了說明問題,我們先來看兩組資料
有兩個表data,link,現在希望更新data表中的amount列值,和link表的credit_class做關聯。
如果data.credit_class=link.credit_class,就直接把data.amount修改為link.score。
邏輯的難點是,如果data.credit_class!=link.credit_class就把data.amount修改為link.credit_class='*'對應的score值
以下面的資料為例,
對於id為1的資料來說,data.credit_class在link.credit_class中存在,所以就需要把amount從110修改為100
對於id未9的資料來說,data.credit_class在link.credit_class中不匹配,所以就找到link.credit_class='*'的值1,然後把data.amount的值從9修改為1
SQL> select *from data;
ID CREDIT_CLASS AMOUNT
---------- ------------------------------ ----------
1 vip 110
2 vip 110
3 vip 110
4 vip 110
5 vip 110
6 vip 110
7 vip 110
8 vip 110
9 normal 9
10 normal 9
10 rows selected.
SQL> select *from link;
CREDIT_CLASS SCORE
------------------------------ ----------
vip 100
agent 10
* 1
關於關聯update的問題,比較經典的錯誤就是
ERROR at line 11:
ORA-01779: cannot modify a column which maps to a non key-preserved table
問題的解釋如下:
SQL> !oerr ora 01779
01779, 00000, "cannot modify a column which maps to a non key-preserved table"
// *Cause: An attempt was made to insert or update columns of a join view which
// map to a non-key-preserved table.
// *Action: Modify the underlying base tables directly.
我們來首先復現一下這個問題,準備基礎的資料。
create table data (id number,credit_class varchar2(100),amount number);
create table link (credit_class varchar2(100),score number);
insert into link values('vip',100);
insert into link values('agent',10);
insert into link values('*',1);
begin
for i in 1..8 loop
insert into data values(i,'vip',110);
end loop;
end;
/
begin
for i in 9..10 loop
insert into data values(i,'normal',9);
end loop;
end;
/
alter table data modify(id primary key); --給表data加上主鍵,可以保證在關聯update能夠校驗唯一性。
有些資料的credit_class匹配link.credit_class,有些不匹配。我們先一股腦把資料都配上,然後再過濾。
select a.id,a.credit_class,x.credit_class ,a.amount,x.score From data a, link x
where (a.credit_class=x.credit_class or x.credit_class ='*')
order by id;
ID CREDIT_CLASS CREDIT_CLASS AMOUNT SCORE
---------- ------------------------------ ------------------------------ ---------- ----------
1 vip vip 110 100
1 vip * 110 1
2 vip vip 110 100
2 vip * 110 1
3 vip * 110 1
3 vip vip 110 100
4 vip * 110 1
4 vip vip 110 100
5 vip * 110 1
5 vip vip 110 100
6 vip vip 110 100
6 vip * 110 1
7 vip vip 110 100
7 vip * 110 1
8 vip vip 110 100
8 vip * 110 1
9 normal * 9 1
10 normal * 9 1
看看id=1和id=9的資料,就能看出差別了。id=1的資料存在匹配的credit_class,而id=9卻沒有匹配的ccredit_class,所以關聯表link之後得到的score也不同。
現在的問題是如何把id=1的資料進行去重。如果存在匹配的credit_class,就修改對應的score為amount值,對於credit_class='*’ and id=1的那條記錄如何做排除是這個問題的關鍵。
嘗試了各種方法之後,發現還是把資料一分為二,creidt_class匹配的一組,credit_class不匹配的一組。根據count(id) 做group by來分組。
--存在匹配credit_class的資料為:
ID OLD CREDIT_CLASS AMOUNT SCORE
---------- ------------------------------ ------------------------------ ---------- ----------
1 vip vip 110 100
6 vip vip 110 100
2 vip vip 110 100
4 vip vip 110 100
5 vip vip 110 100
8 vip vip 110 100
3 vip vip 110 100
7 vip vip 110 100
8 rows selected.
--不匹配credit_class的資料為:
select *from(select x.id,a.credit_class old,x.credit_class ,x.amount,a.score From link a, data x
where (a.credit_class=x.credit_class or a.credit_class ='*') )
where id in(
select id from (
select x.id,a.credit_class old,x.credit_class ,a.score From link a, data x
where (a.credit_class=x.credit_class or a.credit_class ='*')
)group by id having count(*)=1
);
得到的資料如下:
ID OLD CREDIT_CLASS AMOUNT SCORE
---------- ------------------------------ ------------------------------ ---------- ----------
9 * normal 9 1
10 * normal 9 1
得到了資料集,但是使用update語句卻是困難重重。
無論是使用Hint /*+BYPASS_UJVC*/ 還是使用各種可用的update方法。都會丟擲ORA-01779: cannot modify a column which maps to a non key-preserved table
SQL> update (
2 select*from (
3 select x.id,a.credit_class old,x.credit_class ,x.amount ,a.score From link a, data x
4 where (a.credit_class=x.credit_class or a.credit_class ='*') )
5 where id in(
6 select id from (
7 select x.id,a.credit_class old,x.credit_class ,a.score From link a, data x
8 where (a.credit_class=x.credit_class or a.credit_class ='*')
9 )group by id having count(*)=2
10 ) and old!='*'
11 ) set amount=score
12 /
) set amount=score
*
ERROR at line 11:
ORA-01779: cannot modify a column which maps to a non key-preserved table
最後使用的方法是透過pl/sql來完成,寫了一個小的pl/sql指令碼。
declare
cursor part1_cursor is select*from (
select x.id,a.credit_class old,x.credit_class ,x.amount ,a.score From link a, data x
where (a.credit_class=x.credit_class or a.credit_class ='*') )
where id in(
select id from (
select x.id,a.credit_class old,x.credit_class ,a.score From link a, data x
where (a.credit_class=x.credit_class or a.credit_class ='*')
)group by id having count(*)=2
) and old!='*'
;
cursor part2_cursor is
select *from(select x.id,a.credit_class old,x.credit_class ,x.amount,a.score From link a, data x
where (a.credit_class=x.credit_class or a.credit_class ='*') )
where id in(
select id from (
select x.id,a.credit_class old,x.credit_class ,a.score From link a, data x
where (a.credit_class=x.credit_class or a.credit_class ='*')
)group by id having count(*)=1
);
begin
for tmp_part1_cursor in part1_cursor loop
dbms_output.put_line('update data set amount is '||tmp_part1_cursor.score||' for id:'||tmp_part1_cursor.id);
update data set amount=tmp_part1_cursor.score where id=tmp_part1_cursor.id;
rollback;
end loop;
for tmp_part2_cursor in part2_cursor loop
dbms_output.put_line('update data set amount is '||tmp_part2_cursor.score||' for id:'||tmp_part2_cursor.id);
update data set amount=tmp_part2_cursor.score where id=tmp_part2_cursor.id;
rollback;
end loop;
end;
/
指令碼執行結果如下:
update data set amount is 100 for id:1
update data set amount is 100 for id:6
update data set amount is 100 for id:2
update data set amount is 100 for id:4
update data set amount is 100 for id:5
update data set amount is 100 for id:8
update data set amount is 100 for id:3
update data set amount is 100 for id:7
update data set amount is 1 for id:9
update data set amount is 1 for id:10
PL/SQL procedure successfully completed.
可以看到還是很快就處理完成了,而且也沒有多次更新。基本是完成了期望的結果。
透過這個問題,方法總比困難多,一種方式不合適還可以透過其它的方式實現。能夠最大限度的保證資料的準確性才是根本。
create table data (id number,credit_class varchar2(100),amount number);
create table link (credit_class varchar2(100),score number);
insert into link values('vip',100);
insert into link values('agent',10);
insert into link values('*',1);
begin
for i in 1..8 loop
insert into data values(i,'vip',110);
end loop;
end;
/
begin
for i in 9..10 loop
insert into data values(i,'normal',9);
end loop;
end;
/
alter table data modify(id primary key); --給表data加上主鍵,可以保證在關聯update能夠校驗唯一性。
有些資料的credit_class匹配link.credit_class,有些不匹配。我們先一股腦把資料都配上,然後再過濾。
select a.id,a.credit_class,x.credit_class ,a.amount,x.score From data a, link x
where (a.credit_class=x.credit_class or x.credit_class ='*')
order by id;
ID CREDIT_CLASS CREDIT_CLASS AMOUNT SCORE
---------- ------------------------------ ------------------------------ ---------- ----------
1 vip vip 110 100
1 vip * 110 1
2 vip vip 110 100
2 vip * 110 1
3 vip * 110 1
3 vip vip 110 100
4 vip * 110 1
4 vip vip 110 100
5 vip * 110 1
5 vip vip 110 100
6 vip vip 110 100
6 vip * 110 1
7 vip vip 110 100
7 vip * 110 1
8 vip vip 110 100
8 vip * 110 1
9 normal * 9 1
10 normal * 9 1
看看id=1和id=9的資料,就能看出差別了。id=1的資料存在匹配的credit_class,而id=9卻沒有匹配的ccredit_class,所以關聯表link之後得到的score也不同。
現在的問題是如何把id=1的資料進行去重。如果存在匹配的credit_class,就修改對應的score為amount值,對於credit_class='*’ and id=1的那條記錄如何做排除是這個問題的關鍵。
嘗試了各種方法之後,發現還是把資料一分為二,creidt_class匹配的一組,credit_class不匹配的一組。根據count(id) 做group by來分組。
--存在匹配credit_class的資料為:
ID OLD CREDIT_CLASS AMOUNT SCORE
---------- ------------------------------ ------------------------------ ---------- ----------
1 vip vip 110 100
6 vip vip 110 100
2 vip vip 110 100
4 vip vip 110 100
5 vip vip 110 100
8 vip vip 110 100
3 vip vip 110 100
7 vip vip 110 100
8 rows selected.
select *from(select x.id,a.credit_class old,x.credit_class ,x.amount,a.score From link a, data x
where (a.credit_class=x.credit_class or a.credit_class ='*') )
where id in(
select id from (
select x.id,a.credit_class old,x.credit_class ,a.score From link a, data x
where (a.credit_class=x.credit_class or a.credit_class ='*')
)group by id having count(*)=1
);
得到的資料如下:
ID OLD CREDIT_CLASS AMOUNT SCORE
---------- ------------------------------ ------------------------------ ---------- ----------
9 * normal 9 1
10 * normal 9 1
得到了資料集,但是使用update語句卻是困難重重。
無論是使用Hint /*+BYPASS_UJVC*/ 還是使用各種可用的update方法。都會丟擲ORA-01779: cannot modify a column which maps to a non key-preserved table
SQL> update (
2 select*from (
3 select x.id,a.credit_class old,x.credit_class ,x.amount ,a.score From link a, data x
4 where (a.credit_class=x.credit_class or a.credit_class ='*') )
5 where id in(
6 select id from (
7 select x.id,a.credit_class old,x.credit_class ,a.score From link a, data x
8 where (a.credit_class=x.credit_class or a.credit_class ='*')
9 )group by id having count(*)=2
10 ) and old!='*'
11 ) set amount=score
12 /
) set amount=score
*
ERROR at line 11:
ORA-01779: cannot modify a column which maps to a non key-preserved table
最後使用的方法是透過pl/sql來完成,寫了一個小的pl/sql指令碼。
declare
cursor part1_cursor is select*from (
select x.id,a.credit_class old,x.credit_class ,x.amount ,a.score From link a, data x
where (a.credit_class=x.credit_class or a.credit_class ='*') )
where id in(
select id from (
select x.id,a.credit_class old,x.credit_class ,a.score From link a, data x
where (a.credit_class=x.credit_class or a.credit_class ='*')
)group by id having count(*)=2
) and old!='*'
;
cursor part2_cursor is
select *from(select x.id,a.credit_class old,x.credit_class ,x.amount,a.score From link a, data x
where (a.credit_class=x.credit_class or a.credit_class ='*') )
where id in(
select id from (
select x.id,a.credit_class old,x.credit_class ,a.score From link a, data x
where (a.credit_class=x.credit_class or a.credit_class ='*')
)group by id having count(*)=1
);
begin
for tmp_part1_cursor in part1_cursor loop
dbms_output.put_line('update data set amount is '||tmp_part1_cursor.score||' for id:'||tmp_part1_cursor.id);
update data set amount=tmp_part1_cursor.score where id=tmp_part1_cursor.id;
rollback;
end loop;
for tmp_part2_cursor in part2_cursor loop
dbms_output.put_line('update data set amount is '||tmp_part2_cursor.score||' for id:'||tmp_part2_cursor.id);
update data set amount=tmp_part2_cursor.score where id=tmp_part2_cursor.id;
rollback;
end loop;
end;
/
指令碼執行結果如下:
update data set amount is 100 for id:1
update data set amount is 100 for id:6
update data set amount is 100 for id:2
update data set amount is 100 for id:4
update data set amount is 100 for id:5
update data set amount is 100 for id:8
update data set amount is 100 for id:3
update data set amount is 100 for id:7
update data set amount is 1 for id:9
update data set amount is 1 for id:10
PL/SQL procedure successfully completed.
可以看到還是很快就處理完成了,而且也沒有多次更新。基本是完成了期望的結果。
透過這個問題,方法總比困難多,一種方式不合適還可以透過其它的方式實現。能夠最大限度的保證資料的準確性才是根本。
來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/8494287/viewspace-1422003/,如需轉載,請註明出處,否則將追究法律責任。
相關文章
- ActiveMQ問題分析和解決MQ
- MyCAT的常見問題分析和解決
- ORA-01427問題的分析和解決
- JAVA中文比較問題的分析和解決 (轉)Java
- oracle net相關問題的彙總和解決Oracle
- Oracle 惡意攻擊問題分析和解決(一)Oracle
- Oracle資料不同步的問題分析和解決思路Oracle
- 關於session leak的問題分析Session
- composer依賴相關的問題和解決辦法
- Debian 11 關閉 swap 遇到的問題和解決方案
- 關於$ is not defined的原因和解決辦法
- dataguard中MRP無法啟動的問題分析和解決
- 關於all_procedures的問題分析
- 關於SQLRecoverableException問題的排查和分析SQLException
- redis分散式鎖的問題和解決Redis分散式
- 最近遇到的問題和解決辦法
- Julien Codorniou:關於社交手機遊戲的三個問題和解答遊戲
- 關於兩個簡單問題的分析
- 關於無法用 https 登入 SAP ABAP Netweaver 系統的問題和解決方案HTTP
- 粘包問題原因和解決方法
- Redis 常見的效能問題和解決方法Redis
- nginx和Tomcat整合後發生的重定向問題分析和解決NginxTomcat
- 解決Hexo關於圖片的問題Hexo
- 關於解決問題的幾個段位
- 關於Failed to resolve的問題解決AI
- 有關 Android Studio 重複引入包的問題和解決方案Android
- Elasticsearch中關於transform的一個問題分析ElasticsearchORM
- 關於desc的一個奇怪問題及分析
- 關於oracle的索引重建問題及原因分析Oracle索引
- 關於ORA-01555的問題分析
- 怎樣成為解決問題的高手?——關於問題解決的關鍵4步驟
- 透徹分析和解決一切javaWeb專案亂碼問題JavaWeb
- discuz雲平臺報呼叫遠端介面失敗的問題分析和解決
- 關於刪除資料的快慢問題的分析
- Redis常見的效能問題和解決方法UWRedis
- 關於android 使用bitmap的OOM心得和解決方案AndroidOOM
- 關於 LF will be replaced by CRLF 問題的解決方式
- 一個關於/root/.gvfs的問題解決?