無法執行的update問題解析

jeanron100發表於2015-06-04
今天有個同事向我反饋一個問題,說是客戶在部署他們提供的一個sql語句時,報了ora錯誤,想讓我幫忙看看是什麼原因。
update sub_errs set error_status = 'READY_TO_RECYCLE' WHERE sub_appl_id = 3008 AND ENTITY_ID = 8336079 AND MASTER_TRX_ID = 202809364 and error_status = 'PENDING_DEPENDENCY' 
Error report - 
SQL Error: ORA-02290: ????????????????????????????? (APPO.SUB_ERRS_1CK) 
首先看錯誤,還真接上小語種了,自己百度翻譯了下,是違反約束條件的意思,也是開個玩笑,這個問題可以透過oerr來檢視oracle的解釋。
02290, 00000, "check constraint (%s.%s) violated"
// *Cause: The values being inserted do not satisfy the named check 
//      constraint.
// *Action: do not insert values that violate the constraint.

可以看出應該是約束的地方出問題了,從約束的命名來看是以CK結尾,即check constraint。
檢視user_constraints中的search_condition欄位,條件著實複雜,search_condition的條件如下:
(depen_ind='S' AND error_status='PENDING_DEPENDENCY')
OR (depen_ind='S' AND error_status='COMPLETED')
OR (depen_ind='S' AND error_status='PURGED')
OR depen_ind='B'
OR depen_ind='N'

即對於指定的欄位error_status  需要滿足上面的這些條件。這個check constraint算是一個比較複雜的約束了。
可能有些人看這個約束就有點暈,到底是希望滿足條件還是不滿足條件啊。
我們可以類比一下,如果是not null constraint,可能約束就是類似 xxxx is not null的形式,這個時候是希望這個欄位不為空的,即滿足條件。所以這個問題是希望對
error_status  ,滿足上面的條件才行。
給同事解釋了一通,讓他去看看是不是現有環境中存在資料問題,是不是因為資料問題導致條件不滿足的。
結果過了一會同事又過來向我確認,說按照那個條件查出的結果和原有的一致的。都是19條資料。

因為表中的資料很多,所以為了儘快復現這個問題,
自己採用了exp的query選項匯出資料,這樣就會只匯出19條資料,資料量就小多了,匯入到一個測試環境中,就可以大膽的測試了。
exp xxxxx/xxx  file=a.dmp tables=sub_errs query=\" where  sub_appl_id = 3008 AND ENTITY_ID = 8336079 \"

我先根據id來抽取資料,抽取出19條資料來,然後在測試使用者中使用對應的約束進行修改,看看問題是否會復現。
匯入資料很順利,19條資料很快就匯入了。
imp xxxxx/xxxx   file=a.dmp tables=sub_errs indexes=n grants=n ignore=Y constraints=n buffer=9102000 
這個時候嘗試重現,發現問題依舊。

這個時候我們把問題拆分一下,先把update改寫成為select語句。資料條數是19條。
select count(*) from sub_errs WHERE sub_appl_id = 3008 AND ENTITY_ID = 8336079 AND MASTER_TRX_ID = 202809364 and error_status = 'PENDING_DEPENDENCY' 
and
((depen_ind='S' AND error_status='PENDING_DEPENDENCY')
OR (depen_ind='S' AND error_status='COMPLETED')
OR (depen_ind='S' AND error_status='PURGED')
OR depen_ind='B'
OR depen_ind='N')
這個時候其實約束的驗證條件進行校驗 error_status和depen_ind這兩個欄位,因為現有的資料中depen_ind已經是S了,所以資料上沒有問題。
然後我們進一步分析。update會把error_status修改為'READY_TO_RECYCLE',這個時候細看過濾條件中,是沒有符合的error_status校驗的,這個時候depen_ind還是S,就會出問題,
因為這個時候從depen_ind+error_status還是單純考慮depen_ind都是有問題的,和驗證條件是有衝突的。
這個時候因為error_status是READY_TO_RECYCLE',所以第1,2兩個條件不會起作用,考慮第3,4個條件,就會發現沒有匹配的情況。難怪會拋錯。
((depen_ind='S' AND error_status='PENDING_DEPENDENCY')
OR (depen_ind='S' AND error_status='COMPLETED')
OR (depen_ind='S' AND error_status='PURGED')
OR depen_ind='B'
OR depen_ind='N')
明白了這點之後,就提醒同事,指令碼應該存在問題,也修改同時修改depen_ind欄位的值,需要修改為B或者N,這個由他們來做進一步的確認了。
最後修改後的語句為:
update sub_errs set error_status = 'READY_TO_RECYCLE',depen_ind='N'  WHERE sub_appl_id = 3008 AND ENTITY_ID = 8336079 AND MASTER_TRX_ID = 202809364 and error_status = 'PENDING_DEPENDENCY' 
這個問題的解決就告一段落了。
這個案例帶給我的啟示就是看似是約束導致的問題,一般運維人員可能會直接認為是資料問題把問題直接退給開發人員,其實從開發人員的角度來說,去進一步排查這個問題就會顯得很困難,不知道該從何開始。
其實問題最後的原因就是語句的修改不夠規範和全面導致約束的校驗失敗,發現這個問題的過程還是需要一些耐心的。

來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/23718752/viewspace-1686924/,如需轉載,請註明出處,否則將追究法律責任。

相關文章