記一次詭異的Oracle查詢轉換
記一次 詭異 的 Oracle 查詢轉換
說明: 今天 技術群裡有人問 了 一個查詢語句 的問題,這個語句看上去執行肯定會報錯,因為查詢的列名是不存在的,但是實際執行並沒有報錯 , 當然返回的結果也是錯誤的。 使用 10053 看了下 SQL 執行過程中進行了比較詭異的改寫,改變了 SQL 語義,先記錄下來。
環境:
DB:Oracle 11.2.3.0
OS:Redhat 7.5
問題:
已知執行如下SQL 會報錯 ORA-00904 ,報錯原因是因為 dba_objects 表沒有 table_name 欄位。
SQL> select table_name from dba_objects;
那麼如下 SQL 會報錯嗎?
把上面報錯的語句作為子查詢。
select count (*) from dba_tables where TABLE_NAME in ( select TABLE_NAME from dba_objects );
結果是沒有報錯, 可以返回資料:
select count (*) from dba_tables where TABLE_NAME in ( select TABLE_NAME from dba_objects );
問題分析
顯然是優化器對原 SQL 進行了改寫,通過 10053 可以檢視到改寫後的 SQL 語句。
SELECT COUNT(*) "COUNT(*)" FROM "SYS"."USER$" "U", "SYS"."TS$" "TS", "SYS"."SEG$" "S", "SYS"."OBJ$" "CO", "SYS"."TAB$" "T", "SYS"."OBJ$" "O", "SYS"."OBJ$" "CX", "SYS"."USER$" "CU", SYS."X$KSPPCV" "KSPPCV", SYS."X$KSPPI" "KSPPI" WHERE EXISTS ......;
由於dba_tables 和 dba_objects 屬於 sys 使用者下的檢視,引用了很多底層表, 10053 檢視轉換後的 SQL 比較複雜,很難馬上理清各個表關聯關係。
可以先排除複雜檢視的干擾,將 dba_tables和dba_objects資料插入到兩張堆表ttt1和ttt2中,在通過10053檢視ttt1和ttt2查詢改寫後的SQL,結果一目瞭然。
過程如下:
建立ttt1和ttt2表
create table ttt1 as select * from dba_tables ;
create table ttt2 as select * from dba_objects ;
同理ttt2表也沒有table_name列
select TABLE_NAME from ttt2 ;
查詢同樣沒有報錯
select count (*) from ttt1 where TABLE_NAME in ( select TABLE_NAME from ttt2 );
同樣加到子查詢裡可以正常執行
select count (*) from ttt1 where TABLE_NAME in ( select TABLE_NAME from ttt2 );
COUNT (*)
----------
2884
理論上這個SQL 會被優化器改寫,猜想可能會進行子查詢展開,例如改寫成如下 SQL :
SELECT COUNT(*) "COUNT(*)" FROM "CJC"."TTT2" "TTT2", "CJC"."TTT1" "TTT1" WHERE "TTT1"."TABLE_NAME" = "TTT2"."TABLE_NAME";
檢視執行計劃,優化器並沒有執行子查詢展開 ( Subquery Unnesting ) ,而是將 in 條件改寫成 exists
SQL> set autotrace on
SQL> select count(*) from ttt1 where TABLE_NAME in (select TABLE_NAME from ttt2);
生成10053trace 日誌
SQL > alter session set tracefile_identifier = '10053C' ;
SQL > alter session set events '10053 trace name context forever ,level 1' ;
SQL > select count (*) from ttt1 where TABLE_NAME in ( select TABLE_NAME from ttt2 );
COUNT (*)
----------
2884
SQL > alter session set events '10053 trace name context off' ;
檢視 10053trace 日誌
[oracle@cjcos02 trace]$ pwd
/u01/app/oracle/diag/rdbms/cjcdb01/cjcdb01/trace
[oracle@cjcos02 trace]$ vim cjcdb01_ora_13682_10053C.trc
搜尋 關鍵字:Final query after transformations
---原SQL
select count (*) from ttt1 where TABLE_NAME in ( select TABLE_NAME from ttt2 );
---查詢轉換後的SQL
SELECT COUNT(*) "COUNT(*)" FROM "CJC"."TTT1" "TTT1" WHERE EXISTS (SELECT 0 FROM "CJC"."TTT2" "TTT2" WHERE "TTT1"."TABLE_NAME" = "TTT1"."TABLE_NAME");
exists 表示子查詢語句返回結果不為空 , where 條件成立 , 就會執行主sql 語句 , 顯然 "TTT1"."TABLE_NAME" = "TTT1"."TABLE_NAME" 條件無異於where 1=1 ,最終導致對tt1 表進行全表統計,即 select count(*) from ttt1;
顯然是不合理的,本例是一個查詢語句,得出了錯誤的資料,試想下如果是 delete 語句,豈不是誤刪除了所有資料?
為什麼轉換後的謂詞條件變成了: "TTT1"."TABLE_NAME" = "TTT1"."TABLE_NAME" ?
難道不應該是 "TTT1"."TABLE_NAME" = "TTT 2 "."TABLE_NAME" ,然後因為 TTT 2 表沒有 TABLE_NAME 列而報錯嗎?
如果條件是"TTT1"."TABLE_NAME" = "TTT2"."TABLE_NAME" 肯定會報錯:
SELECT COUNT (*) "COUNT(*)"
FROM "CJC"."TTT1" "TTT1"
WHERE EXISTS ( SELECT 0
FROM "CJC"."TTT2" "TTT2"
WHERE "TTT1"."TABLE_NAME" = "TTT2"."TABLE_NAME" );
如何避免這個問題呢?
可以在子查詢列上加上表名,這樣就不會和外層表的列搞混了,例如:
select count (*) from ttt1 where TABLE_NAME in ( select ttt2.TABLE_NAME from ttt2 );
這個問題究竟算不算 BUG 呢?在 MOS 上可以查詢很多關於子查詢的 BUG ,但是沒有找到完全匹配的,先記錄這些,後期有發現在更新吧。
歡迎關注我的微信公眾號"IT小Chen",共同學習,共同成長!!!
來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/29785807/viewspace-2693615/,如需轉載,請註明出處,否則將追究法律責任。
相關文章
- 詭異的”慢查詢“
- Oracle 查詢轉換Oracle
- Oracle 查詢轉換-01 or expansionOracle
- Oracle 查詢轉換-02 View MergingOracleView
- Oracle 查詢轉換-03 Predicate PushingOracle
- Oracle 查詢轉換-04 Subquery UnnestingOracle
- 【SQL】Oracle查詢轉換之 OR用法SQLOracle
- 記一次詭異的故障排查經歷
- Oracle 12CR2查詢轉換之星型轉換Oracle
- 【SQL】Oracle查詢轉換之物化檢視查詢重寫SQLOracle
- 【SQL】Oracle查詢轉換之謂詞推送SQLOracle
- 一次詭異的Oracle使用者無法su問題Oracle
- Oracle 12CR2查詢轉換之臨時錶轉換Oracle
- 【SQL】Oracle查詢轉換之檢視合併SQLOracle
- 記錄一次詭異的拼接sql不生效問題SQL
- 一次分割槽查詢異常的分析
- Oracle 12CR2查詢轉換之謂詞推送Oracle
- Oracle 12CR2查詢轉換之檢視合併Oracle
- Oracle 12CR2查詢轉換之表擴充套件Oracle套件
- 一次詭異的MySQL問題處理故事MySql
- 介面詭異的404問題記錄
- 【恩墨學院】一次由查詢轉換引起的效能問題的分析
- 一次ORACLE字元轉換分析過程Oracle字元
- Oracle 12CR2查詢轉換之cursor-duration臨時表Oracle
- 生產環境一次詭異的空指標問題,竟然反轉了4次指標
- Oracle 查詢Oracle
- 一個詭異的MySQL查詢超時問題,居然隱藏著存在了兩年的BUGMySql
- 記錄一次遞迴查詢的運用遞迴
- 一個詭異的 Pulsar InterruptedException 異常Exception
- [20201214]查詢隱式轉換的sql語句.txtSQL
- 20201214]查詢隱式轉換的sql語句.txtSQL
- D4.玩轉查詢與替換
- Oracle psu查詢Oracle
- oracle 基本查詢Oracle
- oracle常用查詢Oracle
- oracle 精確查詢和模糊查詢Oracle
- oracle學習筆記(十一) 高階查詢Oracle筆記
- 一次線上Redis類轉換異常排查引發的思考Redis