[20181029]避免表示式在sql語句中(10g).txt
[20181029]避免表示式在sql語句中(10g).txt
--//前一段時間的測試,避免表示式在語句中.連結http://blog.itpub.net/267265/viewspace-2215206/
--//在10g存在很明顯的差距,別人也重複了我的測試,給我一些建議.我自己按照自己的想法重新做了1次.
1.環境:
SCOTT@test> @ &r/ver1
PORT_STRING VERSION BANNER
------------------------------ -------------- ----------------------------------------------------------------
x86_64/Linux 2.4.xx 10.2.0.4.0 Oracle Database 10g Enterprise Edition Release 10.2.0.4.0 - 64bi
SCOTT@test> show sga
Total System Global Area 486539264 bytes
Fixed Size 2084872 bytes
Variable Size 360714232 bytes
Database Buffers 113246208 bytes
Redo Buffers 10493952 bytes
--//sga僅僅480M.
$ env | grep NLS
NLS_LANG=AMERICAN_AMERICA.zhs16gbk
NLS_TIMESTAMP_TZ_FORMAT=YYYY-MM-DD HH24:MI:SS.FF TZH:TZM
NLS_TIMESTAMP_FORMAT=YYYY-MM-DD HH24:MI:SS.FF
NLS_DATE_FORMAT=YYYY-MM-DD HH24:MI:SS
--//注:我日期格式定義如上,這樣我下面使用字串變數不會出現問題.
create table t as
with a as (select rownum from dual connect by level<=1e4)
select rownum id ,sysdate-rownum/1440 dd_date from a,a ;
exec sys.dbms_stats.gather_table_stats ( OwnName => user,TabName => 't',Estimate_Percent => NULL,Method_Opt => 'FOR ALL COLUMNS SIZE 1 ',Cascade => True ,No_Invalidate => false);
--//時間有點長,耐心...或者不用全部取樣.
SCOTT@test> select owner, num_rows, blocks, last_analyzed from dba_tables where owner=user and table_name ='T';
OWNER NUM_ROWS BLOCKS LAST_ANALYZED
------ ---------- ---------- -------------------
SCOTT 100000000 263203 2018-10-29 09:20:10
2.測試:
SCOTT@test> select sysdate+10 from dual;
SYSDATE+10
-------------------
2018-11-08 09:21:46
--//測試各種情況:
set timing on
variable v_dd varchar2(20);
exec :v_dd := '2018-11-08 09:21:46'
select count(*) from t where dd_date>=sysdate+10;
select count(*) from t where dd_date>='2018-11-08 09:21:46';
select count(*) from t where dd_date>= (select sysdate+10 from dual);
select count(*) from t where dd_date>= (select sysdate from dual);
select count(*) from t where dd_date>=trunc(sysdate+10);
select count(*) from t where dd_date>=trunc(sysdate);
select count(*) from t where dd_date>=trunc(sysdate);
select count(*) from t where trunc(dd_date)=trunc(sysdate+10);
select count(*) from t where dd_date>=:v_dd;
--// 測試使用等於的情況
select count(*) from t where dd_date='2018-11-08 09:21:46';
select count(*) from t where dd_date=:v_dd;
--//注每條sql語句測試4次,第1次不算.
--//測試sql語句 時間
--------------------------------------------------------------------------------------------------------------------
select count(*) from t where dd_date>=sysdate+10; 00:00:14.28 00:00:14.22 00:00:14.23
select count(*) from t where dd_date>='2018-11-08 09:21:46'; 00:00:03.96 00:00:03.93 00:00:03.96
select count(*) from t where dd_date>= (select sysdate+10 from dual); 00:00:07.64 00:00:07.63 00:00:07.62
select count(*) from t where dd_date>= (select sysdate from dual); 00:00:07.60 00:00:07.61 00:00:07.59
select count(*) from t where dd_date>=trunc(sysdate+10); 00:00:38.58 00:00:38.58 00:00:38.57
select count(*) from t where dd_date>=trunc(sysdate); 00:00:38.58 00:00:38.58 00:00:38.57
select count(*) from t where dd_date>=trunc(sysdate); 00:00:30.02 00:00:30.04 00:00:30.01
select count(*) from t where trunc(dd_date)=trunc(sysdate+10); 00:01:05.22 00:01:05.22 00:01:05.23
select count(*) from t where dd_date>=:v_dd; 00:00:04.89 00:00:04.90 00:00:04.83
--// 測試使用等於的情況
select count(*) from t where dd_date='2018-11-08 09:21:46'; 00:00:03.92 00:00:03.90 00:00:03.93
select count(*) from t where dd_date=:v_dd; 00:00:04.88 00:00:04.90 00:00:04.85
--------------------------------------------------------------------------------------------------------------------
3.結果分析:
--//1.大於 與 等於 類似的語句執行時間差別不大.比如:
select count(*) from t where dd_date>='2018-11-08 09:21:46'; 00:00:03.96 00:00:03.93 00:00:03.96
select count(*) from t where dd_date='2018-11-08 09:21:46'; 00:00:03.92 00:00:03.90 00:00:03.93
--//而且使用常量情況要快於使用繫結變數的情況:
select count(*) from t where dd_date>='2018-11-08 09:21:46'; 00:00:03.96 00:00:03.93 00:00:03.96
select count(*) from t where dd_date>=:v_dd; 00:00:04.89 00:00:04.90 00:00:04.83
--//使用標量子查詢有快取結果的作用,但是測試效果並不明顯.
select count(*) from t where dd_date>= (select sysdate+10 from dual); 00:00:07.64 00:00:07.63 00:00:07.62
select count(*) from t where dd_date>= (select sysdate from dual); 00:00:07.49 00:00:07.48 00:00:07.47
--//2.你可以發現在10g下,如果謂詞裡面存在表示式,如果運算次數很多的情況下,執行時間差距很明顯:
--//3.對比如下:
select count(*) from t where dd_date>=trunc(sysdate); 00:00:30.02 00:00:30.04 00:00:30.01
select count(*) from t where dd_date>=trunc(sysdate+10); 00:00:38.58 00:00:38.58 00:00:38.57
select count(*) from t where trunc(dd_date)=trunc(sysdate+10); 00:01:05.22 00:01:05.22 00:01:05.23
--//可以推測select count(*) from t where trunc(dd_date)=trunc(sysdate+10);,trunc 兩邊都參與運算.
4.繼續分析:
--//建立索引看看.
SCOTT@test> create index i_t_dd_data on t(dd_date) tablespace users_index;
Index created.
SCOTT@test> select min(dd_date) from t;
MIN(DD_DATE)
-------------------
1828-09-10 22:34:03
SCOTT@test> select sysdate - to_date('1828-09-10 22:34:03','yyyy-mm-dd hh24:mi:ss') from dual ;
SYSDATE-TO_DATE('1828-09-1022:34:03','YYYY-MM-DDHH24:MI:SS')
------------------------------------------------------------
69444.4784
SCOTT@test> select segment_name,bytes,blocks from dba_segments where owner=user and segment_name in ('T','I_T_DD_DATA');
SEGMENT_NAME BYTES BLOCKS
-------------------- ---------- ----------
T 2214592512 270336
I_T_DD_DATA 2183135232 266496
--//表比索引僅僅大一點點,索引包含rowid,可以發現索引與表大小相近.
set timing on
--//使用提示保證使用索引範圍掃描.
select /*+ INDEX_RS_ASC(@"SEL$1" "T"@"SEL$1" ("T"."DD_DATE")) */ count(*) from t where dd_date>=sysdate-70000;
SCOTT@test> select /*+ INDEX_RS_ASC(@"SEL$1" "T"@"SEL$1" ("T"."DD_DATE")) */ count(*) from t where dd_date>=sysdate-70000;
COUNT(*)
----------
100000000
Elapsed: 00:00:10.79
--//使用時間 00:00:10.79 00:00:10.83
select /*+ INDEX_RS_ASC(@"SEL$1" "T"@"SEL$1" ("T"."DD_DATE")) */ count(*) from t where dd_date>='1828-09-10 22:34:02';
SCOTT@test> select /*+ INDEX_RS_ASC(@"SEL$1" "T"@"SEL$1" ("T"."DD_DATE")) */ count(*) from t where dd_date>='1828-09-10 22:34:02';
COUNT(*)
----------
100000000
Elapsed: 00:00:10.77
--//使用時間 00:00:10.77 00:00:10.77
--//可以兩者執行時間很接近.我估計這裡根本沒有進行比較,因為只要確定左側,一直掃描到右端就ok了.
--//執行時間主要使用單塊讀方面.可以看出2者執行時間相差不大.
--//修改提示使用索引快速全掃描看看( INDEX FAST FULL SCAN).
SCOTT@test> select /*+ INDEX_FFS(T) */ count(*) from t where dd_date>=sysdate-70000;
COUNT(*)
----------
100000000
Elapsed: 00:00:15.01
SCOTT@test> select /*+ INDEX_FFS(T) */ count(*) from t where dd_date>='1828-09-10 22:34:02';
COUNT(*)
----------
100000000
Elapsed: 00:00:05.75
--//INDEX FAST FULL SCAN是亂序讀取的,這樣必須比較.你可以發現使用表示式以及常量存在10秒的差距.
4.總結:
--//10g下,在執行sql語句時,先snapshot scn取得當日期(sysdate),並且把它當作變數,每次比較都執行sysdate+10 計算.這樣耗費了大量cpu.
--//在謂詞中使用函式問題更加嚴重.
--//當然現在使用10g的越來越少,這樣極端的情況很少見,僅僅透過測試說明問題.
--//實際上如果計算,函式出現在select裡面,如果返回結果集的話,情況也是一樣的.特別有一些開發喜歡自定義的函式.
--//另外在11g以後,這個問題已經不明顯.我僅僅在12c做了測試.
來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/267265/viewspace-2217847/,如需轉載,請註明出處,否則將追究法律責任。
相關文章
- [20180928]避免表示式在sql語句中.txtSQL
- [20181030]避免表示式在sql語句中(10g)(補充).txtSQL
- Oracle SQL 語句中正規表示式的應用OracleSQL
- SQL優化] 避免在WHERE子句中對列使用函式SQL優化函式
- 在 Sql語句中使用正規表示式來查詢你所要的字元SQL字元
- 在sql語句中替換Not In 的方法SQL
- MyBatis在SQL語句中取list的大小MyBatisSQL
- MyBatis的使用三(在sql語句中傳值)MyBatisSQL
- sql語句中as的用法SQL
- SQL語句中直接修改隱含引數,避免影響整個DBSQL
- sql語句中JOIN ON 的使用SQL
- SQL語句中聚合函式忽略NULL值的總結SQL函式Null
- 【開發篇sql】 條件和表示式(八) 10g 正規表示式SQL
- SQL Server SQL語句中的函式呼叫與Oracle SQL語句函式呼叫一個有趣的差別SQLServer函式Oracle
- sql語句中#{}和${}的區別SQL
- sql語句中as的用法和作用SQL
- sql語句中select……as的用法SQL
- PLSQL Language Reference-PL/SQL語言基礎-表示式-PL/SQL表示式中的SQL函式SQL函式
- SQL語句與正規表示式SQL
- SQL語句中exists和in的區別SQL
- SQL語句中not in 和not exist的區別SQL
- sql語句中as的意思是什麼SQL
- sql語句中常量的處理SQL
- 在ORACLE SQL語句中,單引號和雙引號的使用OracleSQL
- 2.在select語句中使用函式(筆記)函式筆記
- PLSQL Language Reference-PL/SQL語言基礎-表示式-BOOLEAN表示式SQLBoolean
- SQL 語句中關於 NULL 的那些坑SQLNull
- SQL語句中NULL的真實含義SQLNull
- 總結SQL語句中的優化提示SQL優化
- 關於在SQL語句中ON和WHERE中條件使用的差異SQL
- SQL語句中SELECT語句的執行順序SQL
- SQL CASE 表示式SQL
- sql語句中where一定要放在group by 之前SQL
- Oracle 10g正規表示式Oracle 10g
- oracle 10g 正規表示式Oracle 10g
- SQL語句中的AND和OR執行順序問題SQL
- Oracle sql 語句中帶有特殊的字元處理OracleSQL字元
- 如何自動填充SQL語句中的公共欄位SQL