[20160706]like % 绑定变量.txt

简介: [20160706]like  % 绑定变量.txt --最近一直在优化一个项目,程序中存在大量的like模糊查询,例子: /* Formatted on 2016/7/6 11:10:55 (QP5 v5.

[20160706]like  % 绑定变量.txt

--最近一直在优化一个项目,程序中存在大量的like模糊查询,例子:
/* Formatted on 2016/7/6 11:10:55 (QP5 v5.252.13127.32867) */
SELECT PATIENT_ID
      ,NAME
      ,RIS_NO
      ,SEX
      ,EXAM_ITEM
      ,DATE_OF_BIRTH
      ,LASTSAVEUSER
      ,EXAM_PARA
      ,PATIENT_LOCAL_ID
      ,MODALITY
      ,EXAM_CLASS
      ,EXAM_SUB_CLASS
      ,DEVICE
      ,RESULT_STATUS
      ,EXAM_DATE_TIME
      ,STUDY_UID
      ,LASTSAVETIME
      ,CHECKUSER
      ,NORMALNAME
      ,CHECKSTATE
      ,OUT_URL
      ,CHECKUSERID
      ,CREATEUSER
      ,CHECKTIME
      ,PRINT_STATUS
      ,IS_ABNORMAL
  FROM REPORTQUERY
WHERE     patient_id LIKE :V001
       AND (LASTSAVETIME BETWEEN :V002 AND :V003)
       AND (checkstate = :V004)
       AND (modality = :V005)
       AND ROWNUM < 1001

SQL_ID        CHILD_NUMBER WAS NAME                   POSITION MAX_LENGTH LAST_CAPTURED       DATATYPE_STRING VALUE_STRING
------------- ------------ --- -------------------- ---------- ---------- ------------------- --------------- --------------------
222m95hh0kvgh            1 YES :V001                         1        128 2016-07-02 10:53:34 VARCHAR2(128)   %TJ1431070%
                           YES :V002                         2         32 2016-07-02 10:53:34 VARCHAR2(32)    2016-07-02 00:00:00
                           YES :V003                         3         32 2016-07-02 10:53:34 VARCHAR2(32)    2016-07-02 23:59:59
                           YES :V004                         4       2000 2016-07-02 10:53:34 VARCHAR2(2000)  40
                           YES :V005                         5        128 2016-07-02 10:53:34 VARCHAR2(128)   US

--//REPORTQUERY是视图,本来选择patient_id是很快的查询,由于前面的%,导致执行计划选择LASTSAVETIME,而这个范围是1天(有时候查询1个月),导致
--//大量的逻辑读。我自己做一些测试想看看使用绑定变量的情况。

1.环境:
SCOTT@book> @ &r/ver1

PORT_STRING                    VERSION        BANNER
------------------------------ -------------- --------------------------------------------------------------------------------
x86_64/Linux 2.4.xx            11.2.0.4.0     Oracle Database 11g Enterprise Edition Release 11.2.0.4.0 - 64bit Production

2.建立测试例子:
create table tx (id number,v varchar2(20),nv nvarchar2(20),pad varchar2(200));
insert into tx select rownum,lpad(rownum,6,'0'),lpad(rownum,6,'0'),lpad('x',200,'x') from dual connect  by level<=1e5;
commit ;
create index i_tx_v on tx(v) PCTFREE 50;
create index i_tx_nv on tx(nv) PCTFREE 50;
execute sys.dbms_stats.gather_table_stats ( OwnName => user,TabName => 'tx',Estimate_Percent => NULL,Method_Opt => 'FOR ALL COLUMNS SIZE 1 ',Cascade => True ,No_Invalidate => false);

3.测试:
SCOTT@book> column pad noprint
SCOTT@book> alter session set statistics_level=all;
Session altered.

SCOTT@book> select * from tx where v like '%000042%';
        ID V                    NV
---------- -------------------- ----------------------------------------
        42 000042               000042

SCOTT@book> @ &r/dpc '' ''
PLAN_TABLE_OUTPUT
-------------------------------------
SQL_ID  c9z5cg37cu8yz, child number 0
-------------------------------------
select * from tx where v like '%000042%'
Plan hash value: 40191160
--------------------------------------------------------------------------------------------------------------------
| Id  | Operation         | Name | Starts | E-Rows |E-Bytes| Cost (%CPU)| E-Time   | A-Rows |   A-Time   | Buffers |
--------------------------------------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT  |      |      1 |        |       |   888 (100)|          |      1 |00:00:00.06 |    3275 |
|*  1 |  TABLE ACCESS FULL| TX   |      1 |   5000 |  1103K|   888   (1)| 00:00:11 |      1 |00:00:00.06 |    3275 |
--------------------------------------------------------------------------------------------------------------------
Query Block Name / Object Alias (identified by operation id):
-------------------------------------------------------------
   1 - SEL$1 / TX@SEL$1
Predicate Information (identified by operation id):
---------------------------------------------------
   1 - filter(("V" LIKE '%000042%' AND "V" IS NOT NULL))

--如果查询select * from tx where v like '000042%';是可以使用索引的。

4.如果使用绑定变量呢?
SCOTT@book> variable x varchar2(20);
SCOTT@book> exec :x := '%000042%';
PL/SQL procedure successfully completed.

SCOTT@book> select * from tx where v like :x;
        ID V                    NV
---------- -------------------- ----------------------------------------
        42 000042               000042

SCOTT@book> @ &r/dpc '' ''
PLAN_TABLE_OUTPUT
-------------------------------------
SQL_ID  87rcszfzcz5gu, child number 0
-------------------------------------
select * from tx where v like :x
Plan hash value: 3964412060
-----------------------------------------------------------------------------------------------------------------------------------------
| Id  | Operation                   | Name   | Starts | E-Rows |E-Bytes| Cost (%CPU)| E-Time   | A-Rows |   A-Time   | Buffers | Reads  |
-----------------------------------------------------------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT            |        |      1 |        |       |   187 (100)|          |      1 |00:00:00.07 |     464 |    461 |
|   1 |  TABLE ACCESS BY INDEX ROWID| TX     |      1 |   5000 |  1103K|   187   (0)| 00:00:03 |      1 |00:00:00.07 |     464 |    461 |
|*  2 |   INDEX RANGE SCAN          | I_TX_V |      1 |   5000 |       |    25   (0)| 00:00:01 |      1 |00:00:00.07 |     463 |    461 |
-----------------------------------------------------------------------------------------------------------------------------------------
Query Block Name / Object Alias (identified by operation id):
-------------------------------------------------------------
   1 - SEL$1 / TX@SEL$1
   2 - SEL$1 / TX@SEL$1
Peeked Binds (identified by position):
--------------------------------------
   1 - (VARCHAR2(30), CSID=852): '%000042%'
Predicate Information (identified by operation id):
---------------------------------------------------
   2 - access("V" LIKE :X)
       filter("V" LIKE :X)

--可以发现绑定变量是使用索引的。有点奇怪的是估计返回相同的行相同,而带入参数选择索引,估计索引群集因子很小。

--把索引建立大一些看看。
SCOTT@book> alter index i_tx_v rebuild pctfree 90;
Index altered.

SCOTT@book> execute sys.dbms_stats.gather_table_stats ( OwnName => user,TabName => 'tx',Estimate_Percent => NULL,Method_Opt => 'FOR ALL COLUMNS SIZE 1 ',Cascade => True ,No_Invalidate => false);
PL/SQL procedure successfully completed.

SCOTT@book> select * from tx where v like :x;
        ID V                    NV
---------- -------------------- ----------------------------------------
        42 000042               000042

SCOTT@book> @ &r/dpc '' ''
PLAN_TABLE_OUTPUT
--------------------------------------
SQL_ID  87rcszfzcz5gu, child number 0
-------------------------------------
select * from tx where v like :x
Plan hash value: 3964412060
--------------------------------------------------------------------------------------------------------------------------------
| Id  | Operation                   | Name   | Starts | E-Rows |E-Bytes| Cost (%CPU)| E-Time   | A-Rows |   A-Time   | Buffers |
--------------------------------------------------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT            |        |      1 |        |       |   307 (100)|          |      1 |00:00:00.07 |    2862 |
|   1 |  TABLE ACCESS BY INDEX ROWID| TX     |      1 |   5000 |  1103K|   307   (0)| 00:00:04 |      1 |00:00:00.07 |    2862 |
|*  2 |   INDEX RANGE SCAN          | I_TX_V |      1 |   5000 |       |   145   (0)| 00:00:02 |      1 |00:00:00.07 |    2861 |
--------------------------------------------------------------------------------------------------------------------------------
Query Block Name / Object Alias (identified by operation id):
-------------------------------------------------------------
   1 - SEL$1 / TX@SEL$1
   2 - SEL$1 / TX@SEL$1
Peeked Binds (identified by position):
--------------------------------------
   1 - (VARCHAR2(30), CSID=852): '%000042%'
Predicate Information (identified by operation id):
---------------------------------------------------
   2 - access("V" LIKE :X)
       filter("V" LIKE :X)
--//这样并没有改变群集因子,cost增加不大。

--//oracle 估计返回5000行,占5000/100000=5%.oracle也是选择索引范围扫描。
--//这种情况是选择索引好还是全表扫描还呢?主要看返回的行数量。
--//oracle很奇怪,前面的直接打入文字变量,估计返回也是5000,但是执行计划选择的是全表扫描。

SCOTT@book> exec :x := '%00%';
PL/SQL procedure successfully completed.

Plan hash value: 3964412060
--------------------------------------------------------------------------------------------------------------------------------
| Id  | Operation                   | Name   | Starts | E-Rows |E-Bytes| Cost (%CPU)| E-Time   | A-Rows |   A-Time   | Buffers |
--------------------------------------------------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT            |        |      1 |        |       |   307 (100)|          |  12520 |00:00:00.08 |    4242 |
|   1 |  TABLE ACCESS BY INDEX ROWID| TX     |      1 |   5000 |  1103K|   307   (0)| 00:00:04 |  12520 |00:00:00.08 |    4242 |
|*  2 |   INDEX RANGE SCAN          | I_TX_V |      1 |   5000 |       |   145   (0)| 00:00:02 |  12520 |00:00:00.07 |    2923 |
--------------------------------------------------------------------------------------------------------------------------------

--执行计划不变。重新建立表ty。大乱顺序,提高索引的群集因子。

SCOTT@book> create table ty as select * from tx order by DBMS_RANDOM.VALUE;
Table created.

create index i_ty_v on ty(v) PCTFREE 10;
create index i_ty_nv on ty(nv) PCTFREE 10;

--分析表,忽略。
SCOTT@book> exec :x := '%00%';
PL/SQL procedure successfully completed.

SCOTT@book> @ &r/dpc '' ''
PLAN_TABLE_OUTPUT
-------------------------------------
SQL_ID  50t5atjp1pjh9, child number 0
-------------------------------------
select * from ty where v like :x
Plan hash value: 1260447134
-----------------------------------------------------------------------------------------------------------------------------
| Id  | Operation         | Name | Starts | E-Rows |E-Bytes| Cost (%CPU)| E-Time   | A-Rows |   A-Time   | Buffers | Reads  |
-----------------------------------------------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT  |      |      1 |        |       |   893 (100)|          |  12520 |00:00:00.07 |    3284 |   3226 |
|*  1 |  TABLE ACCESS FULL| TY   |      1 |   5000 |  1103K|   893   (1)| 00:00:11 |  12520 |00:00:00.07 |    3284 |   3226 |
-----------------------------------------------------------------------------------------------------------------------------
Query Block Name / Object Alias (identified by operation id):
-------------------------------------------------------------
   1 - SEL$1 / TY@SEL$1
Peeked Binds (identified by position):
--------------------------------------
   1 - (VARCHAR2(30), CSID=852): '%00%'
Predicate Information (identified by operation id):
---------------------------------------------------
   1 - filter("V" LIKE :X)

--可以看到群集因子对执行计划影响很大。实际上这种执行计划如果返回行很少,使用索引更佳。

--//我个人的建议最好少用模糊查询,开发往往说这个是用户要求,实际上如果最后很慢了,用户往往抱怨不断。我自己很久以前就遇到类似的情况。
--//我还建议开发可以做一个按钮,缺省做精确查询,用户要模糊查询必须选中它。如果模糊很慢,以后用户自然会少用,可惜大部分开发不愿意做.....
--//当然现在有许多方法来稳定执行计划。sql profile ,spm都可以实现。但是实际上这种是以扫描整个索引为代价,而且是单块读。
--//我以前的做法就是人为修改统计信息,lock统计,增加表的块数量,导致执行计划选择索引,因为大多数情况返回的行很少,很多查询基本没有意义。
--//比如你查询 name like '%李%',这样返回很多行,用户不大可能在里面查找需要的信息。

SCOTT@book> EXECUTE SYS.DBMS_STATS.set_table_stats (OWNNAME=>user, TABNAME=>'ty', numblks=> 8888888);
PL/SQL procedure successfully completed.

SCOTT@book> select * from ty where v like :x;
        ID V                    NV
---------- -------------------- ----------------------------------------
        42 000042               000042

SCOTT@book> @ &r/dpc '' ''
PLAN_TABLE_OUTPUT
-------------------------------------
SQL_ID  50t5atjp1pjh9, child number 1
-------------------------------------
select * from ty where v like :x
Plan hash value: 1299836486
-----------------------------------------------------------------------------------------------------------------------------------------
| Id  | Operation                   | Name   | Starts | E-Rows |E-Bytes| Cost (%CPU)| E-Time   | A-Rows |   A-Time   | Buffers | Reads  |
-----------------------------------------------------------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT            |        |      1 |        |       |  5014 (100)|          |      1 |00:00:00.06 |     255 |      1 |
|   1 |  TABLE ACCESS BY INDEX ROWID| TY     |      1 |   5000 |  1103K|  5014   (1)| 00:01:01 |      1 |00:00:00.06 |     255 |      1 |
|*  2 |   INDEX RANGE SCAN          | I_TY_V |      1 |   5000 |       |    14   (0)| 00:00:01 |      1 |00:00:00.06 |     254 |      0 |
-----------------------------------------------------------------------------------------------------------------------------------------
Query Block Name / Object Alias (identified by operation id):
-------------------------------------------------------------
   1 - SEL$1 / TY@SEL$1
   2 - SEL$1 / TY@SEL$1
Peeked Binds (identified by position):
--------------------------------------
   1 - (VARCHAR2(30), CSID=852): '%000042%'
Predicate Information (identified by operation id):
---------------------------------------------------
   2 - access("V" LIKE :X)
       filter("V" LIKE :X)

--总之,开发写代码不要太随意了,认真思考可能产生的结果。看问题看得长远一些。
--当使用模糊like时,里面的查询条件一定要注意。

目录
相关文章
|
SQL
[20180301]sql profile 非绑定变量.txt
[20180301]sql profile 非绑定变量.txt http://www.itpub.net/thread-2097379-1-1.html 1.环境: SCOTT@book> @ &r/ver1 PORT_STRING            ...
856 0
|
Oracle 关系型数据库 OLAP
[20180224]expdp query 写法问题.txt
[20180224]expdp query 写法问题.txt --//如果使用expdp/impdp导入导出,如果参数复杂,最好的建议使用参数文件.避免各种问题.通过简单的例子说明问题.
1210 0
|
Oracle 关系型数据库 Linux
[20171231]PLSQL使用绑定变量.txt
[20171231]PLSQL使用绑定变量.txt --//一些应用程序开发使用的绑定变量是:1,:2之列的语句,要调优这些语句必须写成如下: 1.环境: SCOTT@book> @ &r/ver1 PORT_STRING              ...
1198 0
|
SQL Oracle 关系型数据库
[20170929]& 代替冒号绑定变量.txt
[20170929]& 代替冒号绑定变量.txt --//我昨天看链接,http://orasql.org/2017/09/27/ampersand-instead-of-colon-for-bind-variables/ --//重复测试: SCOTT@b...
774 0
|
SQL Perl 关系型数据库
[20161230]查看父游标中sql语句.txt
[20161230]查看父游标中sql语句.txt --上午巡检完,无聊,测试使用oradebug下查看sql语句在父游标中的内容.sql语句在执行第一次硬解析时生成父子游标,其中父游标chunk --中保存sql语句,测试通过oradebug下如何查看: 1.
802 0
|
SQL 索引 关系型数据库
[20151221]sql语句优化.txt
[20151221]sql语句优化.txt --自从发现开发乱用distinct以后,链接http://blog.itpub.net/267265/viewspace-1871989/ --我看sql语句特别注意连接多个表,但是显示仅仅一个表的情况,上个星期五,发现一条: sql_id=dpdk3xfd6cvky SELECT EMR_DJMX.
865 0
|
SQL Oracle 关系型数据库
[20150812]关于抓取绑定变量.txt
[20150812]关于抓取绑定变量.txt --通过视图v$sql_bind_capture以及DBA_HIST_SQLBIND可以抓取到sql语句的绑定变量。
783 0
|
SQL 索引 关系型数据库
[20150715]一条sql语句的优化.txt
[20150715]一条sql语句的优化.txt --生产系统发现一条语句。 update presc_check t set t.diagnosis=replace(t.diagnosis,',慢性病drugs','') --第1眼看到的感觉真的很想骂人,什么能没有where条件呢? --我把这个表拷贝过来。
1010 0
|
SQL 测试技术 索引
[20150610]sql的谓词中使用函数.txt
[20150610]sql的谓词中使用函数.txt --程序开发经常可以看到如下的代码: -- trunc(dd_date)=trunc(sysdate),结果导致必须建立函数索引。
962 0
|
SQL
[20150403]修正sql语句.txt
[20150403]修正sql语句.txt --主要在一些调试与优化时加入hint容易. --参考了链接,我自己做了小量的修改. http://blog.itpub.
617 0