SQL限制條件應儘量避免使用SYSDATE(二)
如果可以明確的使用日期常量來表示,那麼就儘量避免使用SYSDATE作為替代。以前寫過一篇SQL中如何處理常量的,其實已經包含了這個含義。
這一篇介紹SYSDATE和常量對執行計劃的影響。
sql語句中常量的處理:http://yangtingkun.itpub.net/post/468/20038
SQL限制條件應儘量避免使用SYSDATE(一):http://yangtingkun.itpub.net/post/468/487542
上一篇文章提到,SYSDATE是一個函式,對於表掃描的每一行比較都需要一次呼叫。因此會影響效能。
除此之外,SYSDATE由於是函式呼叫,很可能使得CBO無法確定查詢限制條件過濾的結果集,而使得CBO選擇與使用常量不同的執行計劃。
[oracle@yans1 ~]$ sqlplus test/test
SQL*Plus: Release 10.2.0.3.0 - Production on 星期二 7月 14 10:00:52 2009
Copyright (c) 1982, 2006, Oracle. All Rights Reserved.
Connected to:
Oracle Database 10g Enterprise Edition Release 10.2.0.3.0 - 64bit Production
With the Partitioning, OLAP and Data Mining options
SQL> set pages 100 lines 120
SQL> create table t (id number, name varchar2(30), created date);
Table created.
SQL> insert into t select rownum, object_name, created from dba_objects;
70739 rows created.
SQL> insert into t select * from t;
70739 rows created.
SQL> insert into t select * from t;
141478 rows created.
SQL> insert into t select * from t;
282956 rows created.
SQL> insert into t select * from t;
565912 rows created.
SQL> insert into t select * from t;
1131824 rows created.
SQL> insert into t select * from t;
2263648 rows created.
SQL> insert into t select * from t;
4527296 rows created.
SQL> commit;
Commit complete.
SQL> create index ind_t_created on t (created);
Index created.
SQL> exec dbms_stats.gather_table_stats(user, 'T')
PL/SQL procedure successfully completed.
建立了測試表、索引後,對錶進行分析。
下面檢查使用sysdate和常量的不同:
SQL> explain plan for select * from t where created > trunc(sysdate) - 30;
Explained.
SQL> select * from table(dbms_xplan.display);
PLAN_TABLE_OUTPUT
-------------------------------------------------------------------------------------------
Plan hash value: 1670768762
--------------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
--------------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 60768 | 2077K| 4407 (1)| 00:01:02 |
| 1 | TABLE ACCESS BY INDEX ROWID| T | 60768 | 2077K| 4407 (1)| 00:01:02 |
|* 2 | INDEX RANGE SCAN | IND_T_CREATED | 61873 | | 84 (2)| 00:00:02 |
--------------------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
2 - access("CREATED">TRUNC(SYSDATE@!)-30)
14 rows selected.
SQL> select to_char(trunc(sysdate) - 30, 'yyyy-mm-dd') from dual;
TO_CHAR(TR
----------
2009-06-14
SQL> explain plan for select * from t where created > to_date('2009-06-14');
Explained.
SQL> select * from table(dbms_xplan.display);
PLAN_TABLE_OUTPUT
--------------------------------------------------------------------------------------------
Plan hash value: 1670768762
--------------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
--------------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 454K| 15M| 5929 (1)| 00:01:24 |
| 1 | TABLE ACCESS BY INDEX ROWID| T | 454K| 15M| 5929 (1)| 00:01:24 |
|* 2 | INDEX RANGE SCAN | IND_T_CREATED | 83260 | | 112 (1)| 00:00:02 |
--------------------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
2 - access("CREATED">TO_DATE('2009-06-14'))
14 rows selected.
雖然使用SYSDATE和使用常量的執行計劃一樣,但是Oracle認為返回記錄數,返回位元組數,以及執行的代價都是有差異的。
SQL> explain plan for select * from t where created > trunc(sysdate) - 45;
Explained.
SQL> select * from table(dbms_xplan.display);
PLAN_TABLE_OUTPUT
--------------------------------------------------------------------------------------------
Plan hash value: 1601196873
--------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
--------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 620K| 20M| 8805 (5)| 00:02:04 |
|* 1 | TABLE ACCESS FULL| T | 620K| 20M| 8805 (5)| 00:02:04 |
--------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
1 - filter("CREATED">TRUNC(SYSDATE@!)-45)
13 rows selected.
SQL> select to_char(trunc(sysdate) - 45, 'yyyy-mm-dd hh24:mi:ss') from dual;
TO_CHAR(TRUNC(SYSDA
-------------------
2009-05-30 00:00:00
SQL> explain plan for select * from t where created > to_date('2009-05-30', 'yyyy-mm-dd');
Explained.
SQL> select * from table(dbms_xplan.display);
PLAN_TABLE_OUTPUT
--------------------------------------------------------------------------------------------
Plan hash value: 1601196873
--------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
--------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 620K| 20M| 8518 (2)| 00:02:00 |
|* 1 | TABLE ACCESS FULL| T | 620K| 20M| 8518 (2)| 00:02:00 |
--------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
1 - filter("CREATED">TO_DATE('2009-05-30 00:00:00', 'yyyy-mm-dd
hh24:mi:ss'))
14 rows selected.
看來Oracle對於SYSDATE的分析還是比較準確的,大部分情況下都和使用常量的結果一致,但是如果將情況變得複雜一些:
SQL> create table t2 as select * from t;
Table created.
SQL> create index ind_t2_created on t2(created);
Index created.
SQL> exec dbms_stats.gather_table_stats(user, 'T2')
PL/SQL procedure successfully completed.
SQL> create view t1 as select * from t
2 union all select * from t2;
View created.
下面對檢視進行查詢:
SQL> explain plan for
2 select count(*) from t1
3 where created >= to_date('2008-1-1', 'yyyy-mm-dd')
4 and created < trunc(sysdate, 'yyyy');
Explained.
SQL> select * from table(dbms_xplan.display);
PLAN_TABLE_OUTPUT
--------------------------------------------------------------------------------------------
Plan hash value: 2892334184
--------------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
--------------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 1 | 8 | 8240 (7)| 00:01:56 |
| 1 | SORT AGGREGATE | | 1 | 8 | | |
|* 2 | FILTER | | | | | |
| 3 | VIEW | T1 | 9219K| 70M| 8240 (7)| 00:01:56 |
| 4 | UNION-ALL PARTITION | | | | | |
|* 5 | FILTER | | | | | |
|* 6 | INDEX FAST FULL SCAN| IND_T_CREATED | 8287K| 63M| 4086 (8)| 00:00:58 |
|* 7 | FILTER | | | | | |
|* 8 | INDEX FAST FULL SCAN| IND_T2_CREATED | 4610K| 35M| 4164 (7)| 00:00:59 |
--------------------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
2 - filter(TO_DATE('2008-01-01 00:00:00', 'yyyy-mm-dd
hh24:mi:ss')
hh24:mi:ss'))
6 - filter("CREATED"
7 - filter(TRUNC(SYSDATE@!,'fmyyyy')>TO_DATE('2008-01-01 00:00:00', 'yyyy-mm-dd
hh24:mi:ss'))
8 - filter("CREATED"
29 rows selected.
SQL> explain plan for
2 select count(*) from t1
3 where created >= to_date('2008-1-1', 'yyyy-mm-dd')
4 and created < to_date('2009-1-1', 'yyyy-mm-dd');
Explained.
SQL> select * from table(dbms_xplan.display);
PLAN_TABLE_OUTPUT
--------------------------------------------------------------------------------------------
Plan hash value: 90982281
------------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
------------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 1 | 8 | 7839 (3)| 00:01:50 |
| 1 | SORT AGGREGATE | | 1 | 8 | | |
| 2 | VIEW | T1 | 9219K| 70M| 7839 (3)| 00:01:50 |
| 3 | UNION-ALL PARTITION | | | | | |
|* 4 | INDEX FAST FULL SCAN| IND_T_CREATED | 8287K| 63M| 3888 (3)| 00:00:55 |
|* 5 | INDEX FAST FULL SCAN| IND_T2_CREATED | 4610K| 35M| 3961 (3)| 00:00:56 |
------------------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
4 - filter("CREATED"
5 - filter("CREATED"
20 rows selected.
顯然Oracle為了第一個SQL可以順利的執行,在索引掃描的外層又巢狀了一層FILTER,而且二者的執行效率也有明顯的差異:
SQL> set timing on
SQL> set autot on
SQL> select count(*) from t1
2 where created >= to_date('2008-1-1', 'yyyy-mm-dd')
3 and created < to_date('2009-1-1', 'yyyy-mm-dd');
COUNT(*)
----------
16619264
Elapsed: 00:00:02.48
Execution Plan
----------------------------------------------------------
Plan hash value: 90982281
------------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
------------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 1 | 8 | 7839 (3)| 00:01:50 |
| 1 | SORT AGGREGATE | | 1 | 8 | | |
| 2 | VIEW | T1 | 9219K| 70M| 7839 (3)| 00:01:50 |
| 3 | UNION-ALL PARTITION | | | | | |
|* 4 | INDEX FAST FULL SCAN| IND_T_CREATED | 8287K| 63M| 3888 (3)| 00:00:55 |
|* 5 | INDEX FAST FULL SCAN| IND_T2_CREATED | 4610K| 35M| 3961 (3)| 00:00:56 |
------------------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
4 - filter("CREATED"
5 - filter("CREATED"
Statistics
----------------------------------------------------------
0 recursive calls
0 db block gets
23752 consistent gets
0 physical reads
0 redo size
518 bytes sent via SQL*Net to client
492 bytes received via SQL*Net from client
2 SQL*Net roundtrips to/from client
0 sorts (memory)
0 sorts (disk)
1 rows processed
SQL> select count(*) from t1
2 where created >= to_date('2008-1-1', 'yyyy-mm-dd')
3 and created < trunc(sysdate, 'yyyy');
COUNT(*)
----------
16619264
Elapsed: 00:00:04.93
Execution Plan
----------------------------------------------------------
Plan hash value: 2892334184
--------------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
--------------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 1 | 8 | 8240 (7)| 00:01:56 |
| 1 | SORT AGGREGATE | | 1 | 8 | | |
|* 2 | FILTER | | | | | |
| 3 | VIEW | T1 | 9219K| 70M| 8240 (7)| 00:01:56 |
| 4 | UNION-ALL PARTITION | | | | | |
|* 5 | FILTER | | | | | |
|* 6 | INDEX FAST FULL SCAN| IND_T_CREATED | 8287K| 63M| 4086 (8)| 00:00:58 |
|* 7 | FILTER | | | | | |
|* 8 | INDEX FAST FULL SCAN| IND_T2_CREATED | 4610K| 35M| 4164 (7)| 00:00:59 |
--------------------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
2 - filter(TO_DATE('2008-01-01 00:00:00', 'yyyy-mm-dd
hh24:mi:ss')
hh24:mi:ss'))
6 - filter("CREATED"
7 - filter(TRUNC(SYSDATE@!,'fmyyyy')>TO_DATE('2008-01-01 00:00:00', 'yyyy-mm-dd
hh24:mi:ss'))
8 - filter("CREATED"
Statistics
----------------------------------------------------------
0 recursive calls
0 db block gets
23752 consistent gets
0 physical reads
0 redo size
518 bytes sent via SQL*Net to client
492 bytes received via SQL*Net from client
2 SQL*Net roundtrips to/from client
0 sorts (memory)
0 sorts (disk)
1 rows processed
這僅僅是將單表掃描的例子變成訪問包含UNION ALL的檢視,如果在加上多表連線查詢等複雜情況,SYSDATE方式帶來的影響可能會更大。
來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/4227/viewspace-609279/,如需轉載,請註明出處,否則將追究法律責任。
相關文章
- 使用 XML 時儘量避免使用的技術XML
- 【SQL】SQL中if條件的使用SQL
- 儘量避免無知者無畏
- Sql中SYSDATE函式的使用方法SQL函式
- 面試官:如何在開發階段就儘量避免寫出慢 SQL ?面試SQL
- sql 使用變數帶入in條件SQL變數
- PL/SQL 條件SQL
- [AlwaysOn] 建立SQL Server高可用性組T-SQL語法:先決條件和限制SQLServer
- 條件變數如何避免丟失通知變數
- 儘量使用 useReducer,不要使用 useStateuseReducer
- PL/SQL 條件控制語句SQL
- SQL Server解惑——查詢條件IN中能否使用變數SQLServer變數
- 條款02: 儘量以const,enum,inline 替換 #defineinline
- Oracle中left join中右表的限制條件Oracle
- 動態SQL-條件分頁SQL
- 通過新增條件優化SQL優化SQL
- SQL-基礎語法 - 條件分支SQL
- 不要輕易在sql中使用uint64 uint做條件引數SQLUI
- 使用pv命令限制SQL文字匯入速度SQL
- ORACLE sql merge into update where條件位置與效能消耗OracleSQL
- Mysql系列第七講 玩轉select條件查詢,避免採坑MySql
- Linux Qt使用POSIX多執行緒條件變數、互斥鎖(量)LinuxQT執行緒變數
- 銀彈谷零程式碼軟體開發套件原生SQL中in條件使用套件SQL
- T-SQL——關於Join on的的連線條件和where的篩選條件的區分SQL
- Exchange限制郵箱使用者每天/每分鐘的傳送郵件數量和速率
- 每個鎖建立多個條件佇列以避免虛假喚醒佇列
- Excel教程——excel如何使用條件格式Excel
- 關聯條件的業務使用
- impdp自動建立使用者前提條件與應用場景
- SAP ABAP DDIC 結構欄位的一些技術限制條件
- SQL-基礎語法 - 條件查詢 - 模糊查詢SQL
- 採用分片技術(Sharding)應對SQL Azure限制JDSQL
- Java 設定Excel條件格式(高亮條件值、應用單元格值/公式/資料條等型別)JavaExcel公式型別
- php中條件語句的使用整理PHP
- 條件渲染
- mybatis條件判斷及動態sql的簡單擴充MyBatisSQL
- SQL-基礎語法 - 條件查詢 - 邏輯運算SQL
- IDC資料中心應具備如下基本條件
- Go的條件判斷語句的使用Go