越界值导致执行计划走错
最近客户生产上遇到一个统计信息陈旧涉及的 越界 值查询 导致 执行 计划 走错的案例: 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 搭載アプリ

AI Clothes Remover
写真から衣服を削除するオンライン AI ツール。

Undress AI Tool
脱衣画像を無料で

Clothoff.io
AI衣類リムーバー

Video Face Swap
完全無料の AI 顔交換ツールを使用して、あらゆるビデオの顔を簡単に交換できます。

人気の記事

ホットツール

メモ帳++7.3.1
使いやすく無料のコードエディター

SublimeText3 中国語版
中国語版、とても使いやすい

ゼンドスタジオ 13.0.1
強力な PHP 統合開発環境

ドリームウィーバー CS6
ビジュアル Web 開発ツール

SublimeText3 Mac版
神レベルのコード編集ソフト(SublimeText3)

ホットトピック











自動化とタスクのスケジューリングは、ソフトウェア開発における反復的なタスクを合理化する上で重要な役割を果たします。 API からのデータの取得、データ処理の実行、定期的な更新の送信など、5 分ごとに実行する必要がある Python スクリプトがあると想像してください。スクリプトを手動で頻繁に実行すると、時間がかかり、エラーが発生しやすくなります。ここでタスクのスケジュール設定が役に立ちます。このブログ投稿では、Python スクリプトを 5 分ごとに実行するようにスケジュールし、手動介入なしで自動的に実行されるようにする方法を説明します。この目標を達成するために使用でき、タスクを効率的に自動化できるさまざまな方法とライブラリについて説明します。 time.sleep() 関数を使用して Python スクリプトを 5 分ごとに実行する簡単な方法は、tim を利用することです。

Python を使用して Linux でスクリプトを作成および実行する方法 Linux オペレーティング システムでは、Python を使用してさまざまなスクリプトを作成および実行できます。 Python は、スクリプト作成をより簡単かつ効率的にするための豊富なライブラリとツールを提供する、簡潔で強力なプログラミング言語です。以下では、Linux で Python を使用してスクリプトを作成および実行する基本的な手順を紹介し、Python をよりよく理解して使用するのに役立つ具体的なコード例をいくつか示します。 Pythonをインストールする

中関村ニュース:4月18日朝、ファーウェイは突然、P70シリーズの携帯電話がパイオニアプランに基づいて正式に販売されると発表した。購入したい友人は、過去の慣例に従って、行動を起こす準備ができている必要がある。非常に人気があり、常に在庫切れになります。今回、Huawei P70シリーズは、純粋を意味するPuraという名前に変更されました。ファーウェイのユー・チェンドン氏は以前、「2012年以来、ファーウェイのPシリーズスマートフォンは忠実なパートナーのようなもので、世界中の何億人ものユーザーとともに数え切れない貴重な時間を過ごし、人生の美しさと興奮を共に目撃してきた」と語った。彼は、ファーウェイのPシリーズを選択するすべてのユーザーから与えられる信頼と愛が強力な原動力に相当し、ファーウェイがイノベーションの道をしっかりと前進するよう常にインスピレーションを与えていると深く感じました。プラとは純粋という意味です。

win11 プレビュー プログラムを終了できませんか? win11 システムを使用すると、コンピュータ上で win11 プレビュー プログラムが起動され、使用できるようになります。ただし、このプレビュー プログラムを使用したくない友人もいます。このプレビュー プログラムが起動できることを願っています。分からない場合は、終了方法は以下のエディター Win11 プレビュー体験プログラムを終了するためのチュートリアルガイドをまとめましたので、興味のある方は以下をご覧ください。 Win11 Insider Program を終了するためのチュートリアル ガイド 1. まず、ショートカット キー「win+i」を押して Windows の設定に入り、「更新とセキュリティ」をクリックします。 2. 次に、図に示すように、左側のタスクバーにある「Windows Insider Program」をクリックします。 3. この時点で、右側にエクスペリエンスが表示されます。

ブラウン フォーサイス検定は、2 つ以上のグループの分散が等しいかどうかを判断するために使用される統計検定です。 Levene の検定では平均値からの絶対偏差が使用されますが、Brown-Forsythe 検定では中央値からの偏差が使用されます。検定で使用される帰無仮説は次のとおりです - H0: グループ (母集団) の分散は等しい. 対立仮説は、分散が等しくないということです - H1: グループ (母集団) の分散は等しくないです。検定を実行すると、各グループの中央値と中央値との相関、桁数の絶対偏差が計算されます。次に、これらの偏差の分散に基づいて F 統計量を計算します。計算された F 統計量が F 分布表の臨界値より大きいと仮定します。この場合、帰無仮説は棄却され、グループの分散は等しくないと結論付けられます。 Python では、sc

Microsoft は本日、Dynamics 365 Customer Service における SharePoint と Copilot の統合の早期プレビューを発表しました。この統合により、カスタマー サービス エージェントは幅広い知識ソースにアクセスできるようになり、生産性が向上し、顧客とのやり取りが改善されます。現在、Dynamics365 Customer Service の Copilot は内部ナレッジ ベースを活用して、カスタマー サービス エージェントにガイダンスを提供します。チャットや電子メールのコンテンツの下書きを提案することで、Copilot はカスタマー サービス チームの生産性を向上させるための重要なツールになりました。ただし、顧客からのフィードバックは、このツールが SharePoint などの外部ソースからの知識を活用する必要があることを示しています。 SharePoint の共同推進統合 このフィードバックに応えて、

ANCOVA (共分散分析) は、分析に共変量を含めることができるため、有用な統計手法です。これにより、補助変数を調整し、グループ間の比較の精度を高めることができます。これらの追加の因子または共変量は、ANCOVA を使用して研究に含めることができます。観察されたグループ間の差異が外部要因によるものではなく、研究における治療または介入によって引き起こされていることを確認するために、ANCOVA を使用してグループ平均に対する共変量の影響を調整できます。これにより、グループ間のより正確な比較が可能になり、変数間の関係についてより信頼性の高い結論が得られます。この記事では、ANCOVA を詳しく見て、Python で実装します。アンコバとは何ですか?共分散分析 (ANCOVA) 法は、2 つ以上のグループを比較します。

ブラウザで PHP コードを記述し、コードが実行されないようにするにはどうすればよいでしょうか?インターネットの普及に伴い、Web開発に触れる人が増え、PHPの学習にも注目が集まっています。 PHP はサーバー側で実行されるスクリプト言語であり、動的な Web ページを作成するためによく使用されます。ただし、演習フェーズでは、ブラウザーで PHP コードを作成して結果を確認できるようにしたいと考えていますが、コードが実行されることは望ましくありません。では、ブラウザで PHP コードを記述し、それが実行されないようにするにはどうすればよいでしょうか?以下、詳細に説明する。初め、
