越界值导致执行计划走错
最近客户生产上遇到一个统计信息陈旧涉及的 越界 值查询 导致 执行 计划 走错的案例: SQL查询 bankdate = '2013/03/19' 就走到了不合适的索引 IDX_DSF_BANKAPPLHISTORY_02. 下面的 执行 计划 可以忽略DSF_BANKCODE这块。 下面的 执行 计划 可以忽略DSF_BANK
最近客户生产上遇到一个统计信息陈旧涉及的越界值查询导致执行计划走错的案例:
SQL查询bankdate >= '2013/03/19' 就走到了不合适的索引IDX_DSF_BANKAPPLHISTORY_02.
下面的执行计划可以忽略DSF_BANKCODE这块。
下面的执行计划可以忽略DSF_BANKCODE这块。
SQL> select *
2 from dsf_bankapplhistory t
3 where (((((bankdate >= '2013/03/19' and
4 recordnum = 'P00Y0ABFG1E220120204358') and monthnm = '16') and
5 bankcode in
6 (select bankcode
7 from dsf_bankcode
8 where (bankcode = '800000000000' or
9 get_bankcode = '800000000000'))) and flag = '2') and
10 opertype = '1')
11 /
Execution Plan
----------------------------------------------------------
Plan hash value: 3875365240
-----------------------------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
-----------------------------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 1 | 631 | 6 (0)| 00:00:01 |
| 1 | NESTED LOOPS SEMI | | 1 | 631 | 6 (0)| 00:00:01 |
|* 2 | TABLE ACCESS BY INDEX ROWID| DSF_BANKAPPLHISTORY | 1 | 605 | 4 (0)| 00:00:01 |
|* 3 | INDEX RANGE SCAN | IDX_DSF_BANKAPPLHISTORY_02 | 1 | | 3 (0)| 00:00:01 |
|* 4 | TABLE ACCESS BY INDEX ROWID| DSF_BANKCODE | 2 | 52 | 2 (0)| 00:00:01 |
|* 5 | INDEX RANGE SCAN | IDX_BANKCODE_1 | 1 | | 1 (0)| 00:00:01 |
-----------------------------------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
2 - filter("FLAG"='2' AND "RECORDNUM"='P00Y0ABFG1E220120204358' AND "MONTHNM"='16' AND
"OPERTYPE"='1')
3 - access("BANKDATE">='2013/03/19')
4 - filter("GET_BANKCODE"='800000000000' OR "BANKCODE"='800000000000')
5 - access("BANKCODE"="BANKCODE")
生产上的这条SQL语句走错了索引,本身应该走到索引IDX_DSF_BANKAPPLHISTORY_10的,缺走了IDX_DSF_BANKAPPLHISTORY_02。
?2个索引的情况如下:
?
? IDX_DSF_BANKAPPLHISTORY_10(RECORDNUM,MONTHNM??)?
? IDX_DSF_BANKAPPLHISTORY_02(BANKDATE)?
如果查询采用bankdate >= '2013/03/18' 就能走到正确的索引。
SQL> select *
2 from dsf_bankapplhistory t
3 where (((((bankdate >= '2013/03/18' and
4 recordnum = 'P00Y0ABFG1E220120204358') and monthnm = '16') and
5 bankcode in
6 (select bankcode
7 from dsf_bankcode
8 where (bankcode = '800000000000' or
9 get_bankcode = '800000000000'))) and flag = '2') and
10 opertype = '1')
11 /
Execution Plan
----------------------------------------------------------
Plan hash value: 1807757810
-----------------------------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
-----------------------------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 1 | 631 | 7 (0)| 00:00:01 |
| 1 | NESTED LOOPS SEMI | | 1 | 631 | 7 (0)| 00:00:01 |
|* 2 | TABLE ACCESS BY INDEX ROWID| DSF_BANKAPPLHISTORY | 1 | 605 | 5 (0)| 00:00:01 |
|* 3 | INDEX RANGE SCAN | IDX_DSF_BANKAPPLHISTORY_10 | 1 | | 4 (0)| 00:00:01 |
|* 4 | TABLE ACCESS BY INDEX ROWID| DSF_BANKCODE | 2 | 52 | 2 (0)| 00:00:01 |
|* 5 | INDEX RANGE SCAN | IDX_BANKCODE_1 | 1 | | 1 (0)| 00:00:01 |
-----------------------------------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
2 - filter("FLAG"='2' AND "BANKDATE">='2013/03/18' AND "OPERTYPE"='1')
3 - access("RECORDNUM"='P00Y0ABFG1E220120204358' AND "MONTHNM"='16')
4 - filter("GET_BANKCODE"='800000000000' OR "BANKCODE"='800000000000')
5 - access("BANKCODE"="BANKCODE")
采用3月18日之前的日期都能走到正确的索引,而采用3月19日及其之后日期都会走到错误的IDX_DSF_BANKAPPLHISTORY_02索引上。
其实3月18日和3月19日走索引idx_dsf_bankapplhistory_10的成本都没有变化。
SQL> select /*+index(t,idx_dsf_bankapplhistory_10)*/*
2 from dsf_bankapplhistory t
3 where (((((bankdate >= '2013/03/18' and
4 recordnum = 'P00Y0ABFG1E220120204358') and monthnm = '16') and
5 bankcode in
6 (select bankcode
7 from dsf_bankcode
8 where (bankcode = '800000000000' or
9 get_bankcode = '800000000000'))) and flag = '2') and
10 opertype = '1');
Execution Plan
----------------------------------------------------------
Plan hash value: 1807757810
-----------------------------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
-----------------------------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 1 | 631 | 7 (0)| 00:00:01 |
| 1 | NESTED LOOPS SEMI | | 1 | 631 | 7 (0)| 00:00:01 |
|* 2 | TABLE ACCESS BY INDEX ROWID| DSF_BANKAPPLHISTORY | 1 | 605 | 5 (0)| 00:00:01 |
|* 3 | INDEX RANGE SCAN | IDX_DSF_BANKAPPLHISTORY_10 | 1 | | 4 (0)| 00:00:01 |
|* 4 | TABLE ACCESS BY INDEX ROWID| DSF_BANKCODE | 2 | 52 | 2 (0)| 00:00:01 |
|* 5 | INDEX RANGE SCAN | IDX_BANKCODE_1 | 1 | | 1 (0)| 00:00:01 |
-----------------------------------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
2 - filter("FLAG"='2' AND "BANKDATE">='2013/03/18' AND "OPERTYPE"='1')
3 - access("RECORDNUM"='P00Y0ABFG1E220120204358' AND "MONTHNM"='16')
4 - filter("GET_BANKCODE"='800000000000' OR "BANKCODE"='800000000000')
5 - access("BANKCODE"="BANKCODE")
SQL> select /*+index(t,idx_dsf_bankapplhistory_10)*/*
2 from dsf_bankapplhistory t
3 where (((((bankdate >= '2013/03/19' and
4 recordnum = 'P00Y0ABFG1E220120204358') and monthnm = '16') and
5 bankcode in
6 (select bankcode
7 from dsf_bankcode
8 where (bankcode = '800000000000' or
9 get_bankcode = '800000000000'))) and flag = '2') and
10 opertype = '1')
11 /
Execution Plan
----------------------------------------------------------
Plan hash value: 1807757810
-----------------------------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
-----------------------------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 1 | 631 | 7 (0)| 00:00:01 |
| 1 | NESTED LOOPS SEMI | | 1 | 631 | 7 (0)| 00:00:01 |
|* 2 | TABLE ACCESS BY INDEX ROWID| DSF_BANKAPPLHISTORY | 1 | 605 | 5 (0)| 00:00:01 |
|* 3 | INDEX RANGE SCAN | IDX_DSF_BANKAPPLHISTORY_10 | 1 | | 4 (0)| 00:00:01 |
|* 4 | TABLE ACCESS BY INDEX ROWID| DSF_BANKCODE | 2 | 52 | 2 (0)| 00:00:01 |
|* 5 | INDEX RANGE SCAN | IDX_BANKCODE_1 | 1 | | 1 (0)| 00:00:01 |
-----------------------------------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
2 - filter("FLAG"='2' AND "BANKDATE">='2013/03/19' AND "OPERTYPE"='1')
3 - access("RECORDNUM"='P00Y0ABFG1E220120204358' AND "MONTHNM"='16')
4 - filter("GET_BANKCODE"='800000000000' OR "BANKCODE"='800000000000')
5 - access("BANKCODE"="BANKCODE")
而3月18日和3月19日走索引idx_dsf_bankapplhistory_02的成本却降低了1,而估算出的基数也降为1,导致CBO选择了这个错误的索引。
SQL> select /*+index(t,idx_dsf_bankapplhistory_02)*/*
2 from dsf_bankapplhistory t
3 where (((((bankdate >= '2013/03/18' and
4 recordnum = 'P00Y0ABFG1E220120204358') and monthnm = '16') and
5 bankcode in
6 (select bankcode
7 from dsf_bankcode
8 where (bankcode = '800000000000' or
9 get_bankcode = '800000000000'))) and flag = '2') and
10 opertype = '1')
11 /
Execution Plan
----------------------------------------------------------
Plan hash value: 3875365240
-----------------------------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
-----------------------------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 1 | 631 | 7 (0)| 00:00:01 |
| 1 | NESTED LOOPS SEMI | | 1 | 631 | 7 (0)| 00:00:01 |
|* 2 | TABLE ACCESS BY INDEX ROWID| DSF_BANKAPPLHISTORY | 1 | 605 | 5 (0)| 00:00:01 |
|* 3 | INDEX RANGE SCAN | IDX_DSF_BANKAPPLHISTORY_02 | 21 | | 3 (0)| 00:00:01 |
|* 4 | TABLE ACCESS BY INDEX ROWID| DSF_BANKCODE | 2 | 52 | 2 (0)| 00:00:01 |
|* 5 | INDEX RANGE SCAN | IDX_BANKCODE_1 | 1 | | 1 (0)| 00:00:01 |
-----------------------------------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
2 - filter("FLAG"='2' AND "RECORDNUM"='P00Y0ABFG1E220120204358' AND "MONTHNM"='16' AND
"OPERTYPE"='1')
3 - access("BANKDATE">='2013/03/18')
4 - filter("GET_BANKCODE"='800000000000' OR "BANKCODE"='800000000000')
5 - access("BANKCODE"="BANKCODE")
SQL> select /*+index(t,idx_dsf_bankapplhistory_02)*/*
2 from dsf_bankapplhistory t
3 where (((((bankdate >= '2013/03/19' and
4 recordnum = 'P00Y0ABFG1E220120204358') and monthnm = '16') and
5 bankcode in
6 (select bankcode
7 from dsf_bankcode
8 where (bankcode = '800000000000' or
9 get_bankcode = '800000000000'))) and flag = '2') and
10 opertype = '1')
11 /
Execution Plan
----------------------------------------------------------
Plan hash value: 3875365240
-----------------------------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
-----------------------------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 1 | 631 | 6 (0)| 00:00:01 |
| 1 | NESTED LOOPS SEMI | | 1 | 631 | 6 (0)| 00:00:01 |
|* 2 | TABLE ACCESS BY INDEX ROWID| DSF_BANKAPPLHISTORY | 1 | 605 | 4 (0)| 00:00:01 |
|* 3 | INDEX RANGE SCAN | IDX_DSF_BANKAPPLHISTORY_02 | 1 | | 3 (0)| 00:00:01 |
|* 4 | TABLE ACCESS BY INDEX ROWID| DSF_BANKCODE | 2 | 52 | 2 (0)| 00:00:01 |
|* 5 | INDEX RANGE SCAN | IDX_BANKCODE_1 | 1 | | 1 (0)| 00:00:01 |
-----------------------------------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
2 - filter("FLAG"='2' AND "RECORDNUM"='P00Y0ABFG1E220120204358' AND "MONTHNM"='16' AND
"OPERTYPE"='1')
3 - access("BANKDATE">='2013/03/19')
4 - filter("GET_BANKCODE"='800000000000' OR "BANKCODE"='800000000000')
5 - access("BANKCODE"="BANKCODE")
3月18日估算出的基数为21,而3月19日估算出的基数为1(实际上这里估算出的基数应该为0,但一般CBO不允许CARDINALITY为0)。
经查询统计信息过于陈旧:
SQL> select table_name,last_analyzed from dba_tables where table_name='DSF_BANKAPPLHISTORY';
TABLE_NAME LAST_ANALYZED
-------------------- -------------------
DSF_BANKAPPLHISTORY 2012-11-25 21:17:26
这很容易让人联想到越界值的查询,对于越界值的查询将会导致选择性的线性降低,如下图所示:
下面的查询可以查询到BANKDATE的最大和最小值。
SQL> declare
2 v_high_date date;
3 v_low_date date;
4 v_high_value dba_tab_col_statistics.high_value%type;
5 v_low_value dba_tab_col_statistics.low_value%type;
6 begin
7 select high_value,low_value into v_high_value,v_low_value
8 from dba_tab_col_statistics
9 where table_name='DSF_BANKAPPLHISTORY' and column_name='BANKDATE';
10 dbms_stats.convert_raw_value(v_high_value,v_high_date);
11 dbms_stats.convert_raw_value(v_low_value,v_low_date);
12 dbms_output.put_line('high date:'||to_char(v_high_date,'YYYY-MM-DD HH24:MI:SS'));
13 dbms_output.put_line('low date:'||to_char(v_low_date,'YYYY-MM-DD HH24:MI:SS'));
14 end;
15 /
high date:2012-11-25 00:00:00
low date:2012-08-03 00:00:00
PL/SQL procedure successfully completed.
为什么3月19日是转折点,下面的查询可以解析这一点:
SQL> select date '2012-11-25'+(date '2012-11-25' - date '2012-08-03') from dual;
DATE'2012-11-25'+11
-------------------
2013-03-19 00:00:00
其实还有一个转折点就是2012-04-11
SQL> select date '2012-08-03'-(date '2012-11-25' - date '2012-08-03') from dual;
DATE'2012-08-03'-(D
-------------------
2012-04-11 00:00:00
下面我们来看看2012-04-11这个查询的执行计划。
SQL> select *
2 from dsf_bankapplhistory t
3 where bankdate
4 recordnum = 'P00Y0ABFG1E220120204358' and monthnm = '16'
5 /
Execution Plan
----------------------------------------------------------
Plan hash value: 471247677
----------------------------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
----------------------------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 1 | 605 | 4 (0)| 00:00:01 |
|* 1 | TABLE ACCESS BY INDEX ROWID| DSF_BANKAPPLHISTORY | 1 | 605 | 4 (0)| 00:00:01 |
|* 2 | INDEX RANGE SCAN | IDX_DSF_BANKAPPLHISTORY_02 | 1 | | 3 (0)| 00:00:01 |
----------------------------------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
1 - filter("RECORDNUM"='P00Y0ABFG1E220120204358' AND "MONTHNM"='16')
2 - access("BANKDATE"
SQL> select *
2 from dsf_bankapplhistory t
3 where bankdate
4 recordnum = 'P00Y0ABFG1E220120204358' and monthnm = '16'
5 /
Execution Plan
----------------------------------------------------------
Plan hash value: 477419360
----------------------------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
----------------------------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 1 | 605 | 5 (0)| 00:00:01 |
|* 1 | TABLE ACCESS BY INDEX ROWID| DSF_BANKAPPLHISTORY | 1 | 605 | 5 (0)| 00:00:01 |
|* 2 | INDEX RANGE SCAN | IDX_DSF_BANKAPPLHISTORY_10 | 1 | | 4 (0)| 00:00:01 |
----------------------------------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
1 - filter("BANKDATE"
2 - access("RECORDNUM"='P00Y0ABFG1E220120204358' AND "MONTHNM"='16')

熱AI工具

Undresser.AI Undress
人工智慧驅動的應用程序,用於創建逼真的裸體照片

AI Clothes Remover
用於從照片中去除衣服的線上人工智慧工具。

Undress AI Tool
免費脫衣圖片

Clothoff.io
AI脫衣器

Video Face Swap
使用我們完全免費的人工智慧換臉工具,輕鬆在任何影片中換臉!

熱門文章

熱工具

記事本++7.3.1
好用且免費的程式碼編輯器

SublimeText3漢化版
中文版,非常好用

禪工作室 13.0.1
強大的PHP整合開發環境

Dreamweaver CS6
視覺化網頁開發工具

SublimeText3 Mac版
神級程式碼編輯軟體(SublimeText3)

自動化和任務調度在簡化軟體開發中的重複任務方面發揮著至關重要的作用。想像一下,有一個Python腳本需要每5分鐘執行一次,例如從API取得資料、執行資料處理或發送定期更新。如此頻繁地手動運行腳本可能非常耗時且容易出錯。這就是任務調度的用武之地。在這篇文章中,我們將探討如何安排Python腳本每5分鐘執行一次,確保它自動運行而無需手動幹預。我們將討論可用於實現此目標的不同方法和程式庫,使您能夠有效地自動化任務。使用time.sleep()函數每5分鐘運行一次Python腳本的簡單方法是利用tim

如何使用Python在Linux中進行腳本編寫和執行在Linux作業系統中,我們可以使用Python編寫並執行各種腳本。 Python是一種簡潔而強大的程式語言,它提供了豐富的函式庫和工具,使得腳本編寫變得更加簡單和有效率。以下我們將介紹在Linux中如何使用Python進行腳本編寫和執行的基本步驟,同時提供一些具體的程式碼範例來幫助你更好地理解和運用。安裝Pytho

中關村消息:4月18日早上,華為突然宣布P70系列手機開啟先鋒計畫正式開售,想要購買的朋友要準備行動起來了,按照以往慣例,華為的旗艦手機非常搶手,會一直處於缺貨狀態。這次華為P70系列改名為Pura,意為純粹。在此前華為餘承東表示:自2012年起,華為P系列智慧型手機便如同忠實的伙伴,伴隨全球億萬用戶度過了無數珍貴時刻,共同見證了生活中的美好與精彩紛呈。他深刻感悟,每位選擇華為P系列的用戶所給予的信任與熱愛,無異於一股強大的推動力,始終鼓舞著華為在創新之路上堅定前行。 Pura的意思是純粹的。

win11預覽體驗計畫退出不了?我們在使用win11系統的時候,電腦中都會推出win11預覽體驗計劃供我們使用,但是有小伙伴不想使用這個預覽體驗計劃,希望可以推出這個預覽體驗計劃,如果你不知道應該如何退出,小編下面整理了退出Win11預覽體驗計畫教學攻略,如果你有興趣的話,小編下面一起往下看看吧!退出Win11預覽體驗計畫教學攻略1、先按下快速鍵「win+i」進入windows設定點選「更新與安全性」。 2、然後點選左側工作列中的“windows預覽體驗計畫”,如圖所示。 3.此時可以看到右側的體驗

Brown-Forsythe檢定是一種統計檢驗,用於確定兩個或多個組別的變異數是否相等。 Levene檢定使用與平均數的絕對偏差,而Brown-Forsythe檢定則使用與中位數的偏差。檢定中使用的原假設如下-H0:組(總體)的方差相等備擇假設是方差不相等-H1:組(群體)的方差不相等為了執行測試,我們計算每組的中位數以及與中位數的絕對偏差。然後我們根據這些偏差的變異數計算F統計量。假設計算出的F統計量大於F分佈表中的臨界值。在這種情況下,我們拒絕原假設並得出結論:各組的變異數不相等。在Python中,sc

Microsoft今天宣布了Dynamics365客戶服務中SharePoint與Copilot整合的早期預覽版。這種整合將使客戶服務代理能夠存取更廣泛的知識來源,從而提高生產力並改善客戶互動。目前,Dynamics365客戶服務中的Copilot利用內部知識庫為客戶服務代理提供指導。透過建議聊天和草稿電子郵件內容,Copilot已成為提高客戶服務團隊生產力的關鍵工具。但是,客戶回饋表明該工具需要利用來自外部資源(如SharePoint)的知識。 SharePoint協同駕駛整合為了回應這些回饋,

ANCOVA(協方差分析)是一種有用的統計方法,因為它可以在分析中包含協變量,這可以幫助調整輔助變量並增加組間比較的精確度。這些額外的因素或協變量可以透過使用ANCOVA將其納入研究中。為了確保觀察到的組間差異是由研究中的治療或介入引起的,而不是由無關因素引起的,可以使用ANCOVA來調整協變量對組別平均值的影響。這可以使組間比較更準確,並給出更可靠的關於變數之間關係的結論。在本文中,我們將仔細研究ANCOVA並在Python中實作。什麼是ANCOVA?協方差分析(ANCOVA)方法比較兩個或多個組別

怎樣在瀏覽器中編寫PHP程式碼並保持程式碼不被執行?隨著網路的普及,越來越多的人開始接觸網頁開發,其中對於PHP的學習也越來越受到關注。 PHP是一種在伺服器端運行的腳本語言,通常用於編寫動態網頁。然而,在練習階段,我們希望能夠在瀏覽器中編寫PHP程式碼並查看結果,但又不希望程式碼被執行。那麼,如何實現在瀏覽器中編寫PHP程式碼並保持不被執行呢?以下將詳細介紹。首先,
