资讯专栏INFORMATION COLUMN

深入解析Binding In List

IT那活儿 / 205人阅读
深入解析Binding In List

点击上方“IT那活儿”,关注后了解更多精彩内容!!

前  言




在系统开发中,经常有这样的需求:前台传入一个字符串,而且此字符串具有指定分隔符,并且长度不定,那么如何根据传入的这个字符串查询对应的结果呢?考虑这样的需求,你肯定已经想到,采用构造SQL语句来解决,的确,你的想法没错,最简单的也就是构造SQL:

--将字符串转换为convert_value_list,convert_

value_list类似于值的列表,比如将a,b,c转为a,b,c

SELECT ….. FROM ….. WHERE column in (convert_value_list);

的确可以通过构造SQL来解决这样的问题(比如在JAVA中可以将传入的字符串通过String的split方法处理,然后将结果拼凑到SQL中),但是另一方面,这样的写法有一定的限制:Oracle WHERE条件中IN列表数目不能超过1000个,另外列表数目不定会导致无法使用绑定变量而影响效率。

那么怎样才能使列表长度不定而又能使用绑定变量呢?解决方法有很多种,下面逐一分析,从而使你能够根据实际情况来选择何种方法(动态SQL构造也会作为例子进行反面探讨,这个例子在PL/SQL中实现,当然在JAVA等语言中实现方式也类似)。

深入解析



解决binging in list问题,首要考虑的两个问题就是解决列表长度问题和效率问题,效率问题首要考虑绑定变量问题,另外还要考虑比如cardinality(基数)对执行计划的影响等

为了避免复杂性,测试的大部分例子只根据binging in list计算COUNT。

建立测试表:

--建两个表xy和yz作为测试用,所以这两个表很简单,不需要数据。

DROP TABLE xy;
CREATE TABLE xy(id NUMBER);
DROP TABLE yz;
CREATE TABLE yz(id NUMBER);

--将all_objects copy到表t中,以便测试。

DROP TABLE t;
CREATE TABLE t AS SELECT * FROM all_objects;
CREATE INDEX idx_t ON t(object_name);

--分析表和索引。

BEGIN
DBMS_STATS.GATHER_TABLE_STATS(ownname => USER,tabname => t);
DBMS_STATS.GATHER_INDEX_STATS(ownname => USER,indname => idx_t);
END;
/

1. 问题引入:动态SQL构造

本节主要研究动态SQL解决binging in list问题以及相关分析。

下面使用一个简单的拼凑条件进行初步试验,这里我使用的SQL是静态SQL,看看会发生什么情况?

DINGJUN123>SET SERVEROUTPUT ON SIZE 10000
DINGJUN123>DECLARE
2    v_condition VARCHAR2(100);
3    v_sql VARCHAR2(1000);
4    v_count NUMBER(10);
5  BEGIN
6    v_condition := XY || , || YZ; --本意是拼凑XY,YZ,有很多人会写成XY,YZ
7    SELECT COUNT(*)
8      INTO v_count
9      FROM t
10     WHERE object_name IN (v_condition);
11    --打印结果
12    DBMS_OUTPUT.PUT_LINE(v_count);
13    --打印SQL
14    v_sql := SELECT COUNT(*) FROM t WHERE object_name IN ( ||
15             v_condition || );
16    DBMS_OUTPUT.PUT_LINE(v_sql);
17  END;
18  /
0
SELECT COUNT(*) FROM t WHERE object_name IN (XY,YZ)

从上面的结果看到,通过拼凑的SQL,打印出来的是SELECT COUNT(*) FROM t WHERE object_name IN (XY,YZ),看似正确的,但是为什么执行结果是0呢?下面分析一下,执行此SQL:

DINGJUN123>SELECT COUNT(*)
2  FROM t
3  WHERE object_name IN (XY,YZ);
COUNT(*)
----------
2
已选择 1 行。

的确是有结果的,但是为什么在PL/SQL中执行拼凑的静态SQL没有结果呢?原因在于在PL/SQL中打印出的SQL不是真正执行的SQL,打印的是动态拼凑的SQL,而真正执行的是静态SQL,注意:

SELECT COUNT(*) INTO  v_count FROM t WHERE object_name IN  (v_condition);

v_condition是一个varchar2类型,在静态SQL中拼凑的条件相当于一个整体,XY,YZ是一个字符串,在SQL中相当于XY,YZ,因此实际执行的SQL是:

SELECT COUNT(*) FROM t WHERE object_name IN (XY,YZ); //返回0

而不是:

SELECT COUNT(*) FROM t WHERE object_name IN (XY,YZ); //返回2

因此没有找到数据。

这个错误很多人初次解决类似问题会碰到,而且可能百思不得其解,通过上面的分析,你可能已经发现静态SQL与动态SQL有很多不同的地方值得注意。

使用动态SQL,就可以正确查询结果了,如下:

DINGJUN123> DECLARE
2     v_condition VARCHAR2(100);
3     v_sql VARCHAR2(1000);
4     v_count NUMBER(10);
5   BEGIN
6     v_condition:=XY||,||YZ;
7     --放入动态SQL中,结果正确
8     v_sql:=SELECT COUNT(*) FROM t WHERE object_name IN (||v_condition||);
9     EXECUTE IMMEDIATE v_sql INTO v_count;
10     DBMS_OUTPUT.PUT_LINE(v_count);
11     DBMS_OUTPUT.PUT_LINE(v_sql);
12   END;
13   /
2
SELECT COUNT(*) FROM t WHERE object_name IN (XY,YZ)

PL/SQL 过程已成功完成。

现在的结果和预期结果一致,查询返回2。动态SQL的确可以解决这个问题,但是动态SQL会拼凑很多常量,而且数目不定,会导致无法使用绑定变量而影响效率(可能你认为可以使用动态SQL的USING,这是不行的,因为你不知道要绑定多少变量),而且IN中列表数目限制最大是1000。所以,针对这种方法,在实际开发中不推荐使用。

2. 使用正则表达式解决

Oracle 10G支持正则表达式的确给程序开发带来了很大方便,正则表达式是字符串处理的利器,Perl、JAVA、JAVASCRIPT等主流开发语言都支持正则表达式,Oracle也意识到正则表达式的重要性,所以在10G中也引入了对正则表达式的支持。在本节中将使用正则表达式REGEXP_SUBSTR,将按指定分隔符组成的字符串转为中间查询结果集,然后使用子查询(IN、EXISTS)或JOIN解决binging in list问题,当然一般都是使用IN子查询,因为一般不可能选择很多个值。对于正则表达式,如需详细了解,请参考官方文档,使用正则表达式解决binging in list问题的方法如下:

DINGJUN123>VAR str VARCHAR2(100);

DINGJUN123>
EXEC :str := XY,YZ;

PL/SQL 过程已成功完成。

DINGJUN123>SELECT COUNT(*)
2   FROM t
3   WHERE object_name IN
4   (
5     SELECT REGEXP_SUBSTR(:str, [^,]+, 1, LEVEL) AS value_str
6     FROM DUAL
7     CONNECT BY LEVEL <=
8     LENGTH(TRIM(TRANSLATE(:str,TRANSLATE(:str, ,, ), ))) + 1
9   );

COUNT(*)
----------
2
已选择 1 行。

上面的SQL使用REGEXP_SUBSTR将逗号分隔的字符串转为行结果集,其中LENGTH(TRIM(TRANSLATE(:str,TRANSLATE(:str, ,, ), ))) + 1就是查询出有多少个值列表(注意 是空格),当然,也可以使用LENGTH(:str)-LENGTH(REPLACE(:str,,,))+1实现(这里的是空字符串,相当于NULL),这种方法在10G环境中的大部分情况下是可以使用的,好处是可以用到绑定变量,而且列表数可以超过1000个。这里的正则表达式的子查询还是有点复杂的,下一节会讲解如何将子查询封装为一个动态视图,从而屏蔽子查询的复杂性。

3. 使用常规字符串函数以及动态视图

上一节使用正则表达式解决binging in list问题,但是如果你的Oracle版本较低(10G之前),无法使用正则表达式怎么办?那么就可以用本节的知识来解决了,使用INSTR、SUBSTR等函数处理指定分隔符的字符串,将字符串按分隔符转为行,这样就可以像上一节那样处理了。

首先要解决的问题就是如何使用INSTR、SUBSTR等函数将字符串按分隔符转为多行记录,比如对于ab,bc,cd这个字符串来说,要转为3行记录分别为ab、bc、cd,如何转换呢?一般要用到CONNECT BY的,试想如果将字符串转为,ab,bc,cd,,那么就很好转换了,找第1个值ab就是从第1个逗号后面的位置开始,然后截取的长度就是第2个逗号位置-第1个逗号位置-1,其他值类似,有了这个分析,就能很好实现这个需求了:

DINGJUN123>VAR str VARCHAR2(100);

DINGJUN123>
EXEC :str := ab,bc,cd;

PL/SQL 过程已成功完成。

-- LENGTH(:str)-LENGTH(REPLACE(:str,,,))+1是计算有多少个值,和前一节的TRANSLATE一样。

DINGJUN123>SELECT
2   SUBSTR (inlist,
3         INSTR (inlist, ,, 1, LEVEL ) + 1,
4         INSTR (inlist, ,, 1, LEVEL+1)
5         - INSTR (inlist, ,, 1, LEVEL) -1 )
6    AS value_str
7    FROM (SELECT ,||:str||, AS inlist
8            FROM DUAL)
9   CONNECT BY LEVEL <=
10   LENGTH(:str)-LENGTH(REPLACE(:str,,,)) + 1;

VALUE_STR
------------------------------------------------------------
ab
bc
cd

已选择3行。

有了上面的结果作为子查询就和正则表达式一样可以解决binging in list问题,在上一节我说过,这样的子查询可能会很复杂,为了隐藏子查询的复杂性,可以将子查询封装为一个动态视图,所谓动态视图就是传入不同的字符串,视图的结果是不同的,那么如何实现动态视图功能呢?

在PL/SQL中有内置包DBMS_SESSION,这个包的方法SET_CONTEXT可以创建绑定名字的上下文,并且具有属性名和属性值,通过SYS_CONTEXT函数就可以获取指定上下文的属性值。这样只要视图中的字符串值是通过SYS_CONTEXT获取的就可以了,每次调用存储过程重置CONTEXT。注意创建CONTEXT必须在一个命名过程或包过程中调用DBMS_SESSION.SET_CONTEXT,而不能在匿名过程中直接使用DBMS_SESSION.SET_CONTEXT,对于DBMS_SESSION包的详细使用请参考相关文档。详细如下:

3.1 创建上下文

--这个上下文的名字是INLIST_CTX,需要由过程SET_INLIST_CTX_PRC创建。

DINGJUN123>CREATE OR REPLACE CONTEXT INLIST_CTX USING set_inlist_ctx_prc;

上下文已创建。

3.2 建立与上下文创建相关的过程

DINGJUN123>CREATE OR REPLACE PROCEDURE set_inlist_ctx_prc(p_val IN VARCHAR2)
2  /**
3 ||程序说明:
4 ||上下文INLIST_CTX属性名为STR
5 ||p_val为属性对应的值
6 **/

7  AS
8  BEGIN
9    DBMS_SESSION.set_context(INLIST_CTX, STR, p_val);
10  END;
11  /

过程已创建。

3.3 建立视图

--创建动态视图,让SYS_CONTEXT动态给视图传参,只需要将前面语句中的绑定变量:str改为SYS_CONTEXT(INLIST_CTX, STR)就可以了。

DINGJUN123>CREATE OR REPLACE VIEW v_inlist
2  AS
3   SELECT
4   SUBSTR (inlist,
5         INSTR (inlist, ,, 1, LEVEL ) + 1,
6         INSTR (inlist, ,, 1, LEVEL+1)
7         - INSTR (inlist, ,, 1, LEVEL) -1 )
8    AS value_str
9    FROM (SELECT ,||SYS_CONTEXT(INLIST_CTX, STR)||,
10             AS inlist
11            FROM DUAL)
12   CONNECT BY LEVEL <=
13   LENGTH(SYS_CONTEXT(INLIST_CTX, STR))
14   -LENGTH(REPLACE(SYS_CONTEXT(INLIST_CTX, STR),,,))+1;

视图已创建。

3.4 测试

下面测试此动态视图,看是否满足要求:

--创建上下文,并给予属性STR初始值为ab,bc,cd;

DINGJUN123>EXEC set_inlist_ctx_prc(ab,bc,cd);

PL/SQL 过程已成功完成。

--视图成功输出3行记录:

DINGJUN123>SELECT value_str
2 FROM v_inlist;
VALUE_STR
--------------
ab
bc
cd
已选择3行。

--修改上下文的属性值,则视图也改变:

DINGJUN123>EXEC set_inlist_ctx_prc(x,y,z);

PL/SQL 过程已成功完成。

DINGJUN123>SELECT value_str
2 FROM v_inlist;
VALUE_STR
---------------
x
y
z
已选择3行。

通过测试发现,动态视图正常工作,而且因为保存在CONTEXT内的属性是在SESSION范围内的,具有很好的并发性。

下面就用这个动态视图实现本章讨论的binging in list问题,其实很简单,只要将视图放入到子查询中即可,如下:

--先重置CONTEXT

DINGJUN123>EXEC set_inlist_ctx_prc(XY,YZ);

PL/SQL 过程已成功完成。

DINGJUN123>SELECT COUNT(*)
2  FROM t
3  WHERE object_name IN
4  (
5   SELECT value_str
6   FROM v_inlist
7  );
COUNT(*)
----------
2
已选择 1 行。

这个查询是符合要求的,使用动态视图,可以隐藏查询的复杂性,只需要每次查询前调用存储过程重置CONTEXT即可,而且和正则表达式一样,列表数目可以不定,也使用到了绑定变量。本节主要讨论使用INSTR+SUBSTR代替正则表达式在低版本Oracle中的使用,并且介绍了使用DBMS_SESSION包创建CONTEXT和建立动态视图放入子查询中隐藏查询复杂性的方法。

4. 使用集合构造伪表

对这类问题的常规解法,比如Oracle版本是9i,可以使用PL/SQL中的集合类型,对传入的字符串按分隔符解析之后存储到相关集合类型的变量中,比如可以存储到嵌套表,数组中(注意不能是INDEX BY表,必须是SCHEMA级别的类型,数组有容量也不常使用),然后利用TABLE函数将集合转为伪表,剩下就和前面说的一样了。

试想一下,使用集合构造临时表,需要做哪些工作呢?

4.1 外界传入的是一个含有分隔符(一般是逗号,确保字段中没有逗号,如果有,用其他分隔符)的字符串,比如’aa,bb,cc’之类的字符串,首先需要按分隔符解析,然后将每个值存储到对应的集合变量中,所以,需要有一个函数能够接收传入的字符串,然后解析并存储到相应的集合变量并且返回。

4.2 将集合变量通过TABLE函数转换为伪表,放到子查询中。table函数将集合转为伪表,返回的列名是COLUMN_VALUE,对应的类型是集合元素的类型,本节例子的COLUMN_VALUE的类型就是VARCHAR2类型。

4.3 然后写相应的查询语句。

根据上面的描述,需要创建嵌套表以及将字符串转为嵌套表的函数,如下所示:

--创建嵌套表:

CREATE OR REPLACE TYPE varchar2_tt AS TABLE
OF VARCHAR2 (1000);
/

--创建函数:

CREATE OR REPLACE
FUNCTION f_str2list( in_str IN VARCHAR2 ,in_delimiter IN
VARCHAR2 DEFAULT , )
RETURN varchar2_tt
/******************************************************************************
||程序说明:将按指定分隔符分割的字符串转为嵌套表类型变量返回
||输入变量:
|| in_str 字符串,如a,b,c
|| in_delimiter 分割符,默认是逗号
||输出变量:
|| varchar2_tt类型,嵌套表
******************************************************************************/
AS
v_str VARCHAR2(32767) DEFAULT in_str ||in_delimiter;
v_result varchar2_tt := varchar2_tt();
i NUMBER;
BEGIN
LOOP
EXIT WHEN v_str IS NULL;
i := INSTR( v_str, in_delimiter );
v_result.extend;
v_result(v_result.count) :=
TRIM( SUBSTR( v_str, 1, i -1 ) );
v_str := SUBSTR( v_str, i +1 );
END LOOP;
RETURN v_result;
END;
/

执行上面的脚本,创建嵌套表和返回嵌套表的函数即可。

下面使用上面创建的嵌套表和函数来解决binging in list的问题。通过函数将传入的字符串包装成嵌套表,然后利用TABLE函数将嵌套表转为伪表,放到子查询中即可。具体操作如下:

DINGJUN123>VAR str VARCHAR2(100);

DINGJUN123>
EXEC :str :=XY,YZ;

PL/SQL 过程已成功完成。

DINGJUN123>SELECT  COUNT(*)
2  FROM t
3  WHERE object_name IN
4  (SELECT column_value
5  FROM TABLE( CAST(f_str2list(:str) AS varchar2_tt )
6            )
7  );
COUNT(*)
----------
2
已选择 1 行。

结果是正确的。如果传入的不是字符串,而是一个SQL语句或REF CURSOR变量,可以吗?当然可以,把f_str2list函数改改就可以了,这个读者可以自己思考一下,这里不详细讲解。下节讨论binging in list需要注意的性能问题。

5. 使用管道函数解决in list问题

其中 “4. 使用集合构造伪表” 是使用普通的集合函数解决in list问题,其实使用管道函数更好,因为管道函数采用流的形式实时传输数据,这样不需要等集合结果全部完成即可计算。将  4. 使用集合构造伪表” 的函数改为管道函数即可:

CREATE OR REPLACE
FUNCTION f_str2list( in_str IN VARCHAR2 ,in_delimiter IN VARCHAR2 DEFAULT , )
RETURN varchar2_tt PIPELINED
AS
v_str VARCHAR2(32767) DEFAULT in_str ||
in_delimiter;
i NUMBER;
BEGIN
LOOP
EXIT WHEN v_str IS NULL;
i := INSTR( v_str, in_delimiter );
 PIPE ROW (TRIM(SUBSTR(v_str, 1, i - 1)));
v_str := SUBSTR(v_str, i + 1);
END LOOP;
RETURN;
END;
/


试结果完全一样:


dingjun123@ORADB> SELECT count(*)
2      FROM t
3      WHERE object_name IN
4      (SELECT column_value
5      FROM TABLE( CAST(f_str2list(:str) AS varchar2_tt )
6                )
7     );
COUNT(*)
----------
2

6. Binging in list性能问题

Binging in list问题要特别注意性能问题,一般选择的字段都建有索引,希望通过nested loop方式连接查询,而不希望通过hash join或sort merge join方式连接查询,因为实际中传入的一般都不是很长的字符串。本部分主要探讨使用集合函数解决binging in list问题中注意的相关问题,对于正则表达式和INSTR+SUBSTR也可以通过hint来固定计划,比较简单,所以只探讨集合函数的性能问题,请看:

DINGJUN123>SELECT COUNT(*) FROM t;
COUNT(*)
----------
14006

已选择 1 行。
DINGJUN123>SET AUTOTRACE TRACEONLY

DINGJUN123>
VAR str VARCHAR2(100);

DINGJUN123>
EXEC :str :=XY,YZ;

PL/SQL 过程已成功完成。

DINGJUN123> SELECT *
2 FROM t
3 WHERE object_name IN
4 (SELECT column_value
5 FROM TABLE( CAST( f_str2list(:str) AS varchar2_tt ))
6 );
已选择2行。

执行计划:

----------------------------------------------------------

Plan hash value: 3487633200

-------------------------------------------------------------------------------------------------

| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |

-------------------------------------------------------------------------------------------------

| 0 | SELECT STATEMENT | |     1 | 88 |    84   (3)| 00:00:02 |

|* 1 |  HASH JOIN RIGHT SEMI | |     1 | 88 |    84   (3)| 00:00:02 |

| 2 |   COLLECTION ITERATOR PICKLER FETCH| F_STR2LIST |       | |            | |

| 3 |   TABLE ACCESS FULL | T | 14006 | 1176K|    54   (2)| 00:00:01 |
-------------------------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
1 - access("OBJECT_NAME"=VALUE(KOKBF$))


统计信息:


----------------------------------------------------------
927  recursive calls
0  db block gets
 486  consistent gets
233  physical reads
0  redo size
1257  bytes sent via SQL*Net to client
384  bytes received via SQL*Net from client
2  SQL*Net roundtrips to/from client
8  sorts (memory)
0  sorts (disk)
2  rows processed

第1次硬解析,为了比较再次执行,统计信息为:

----------------------------------------------------------
0  recursive calls
0  db block gets
  184  consistent gets
0  physical reads
0  redo size
1257  bytes sent via SQL*Net to client
384  bytes received via SQL*Net from client
2  SQL*Net roundtrips to/from client
0  sorts (memory)
0  sorts (disk)
2  rows processed

从上面结果看到,上面SQL采用的是Hash join的连接方式,全表访问表t,第1次执行逻辑读很大,为486,第2次再执行,逻辑读为184,平均每行逻辑读为92(184/2),这种计划是很差的。那为什么Oracle会采用这种计划呢?如下分析:

DINGJUN123>SELECT/*+first_rows*/ *

2 FROM TABLE( CAST( f_str2list(:str) AS varchar2_tt ));

已选择2行。

执行计划:

----------------------------------------------------------

Plan hash value: 2025561284

------------------------------------------------------------------------------------------------

| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |

------------------------------------------------------------------------------------------------

| 0 | SELECT STATEMENT | |  8168 | 16336 |    29   (0)| 00:00:01 |

| 1 |  COLLECTION ITERATOR PICKLER FETCH| F_STR2LIST |       | |            | |

------------------------------------------------------------------------------------------------

从上面结果看出,TABLE函数的默认行数是8168行(TABLE函数创建的伪表是没有统计信息的),这个值不小了,一般比实际应用中的行数要多的多,经常导致执行计划走hash join,而不是nested loop。怎么改变这种情况呢?当然是加hint提示来改变执行计划了,对binging in list,常常使用的hint有:first_rows,index,cardinality等。这里特别介绍下cardinality(table|alias,n),这个hint很有用,它可以让CBO优化器认为表的行数是n,这样就可以改变执行计划了。现在改写上面的查询:

DINGJUN123>SELECT/*+cardinality(tab,5)*/ column_value

2 FROM TABLE( CAST( f_str2list(:str) AS varchar2_tt )) tab;

已选择2行。

执行计划:

----------------------------------------------------------

Plan hash value: 2025561284

------------------------------------------------------------------------------------------------

| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |

------------------------------------------------------------------------------------------------

| 0 | SELECT STATEMENT | |     5 | 10 |    29   (0)| 00:00:01 |

| 1 |  COLLECTION ITERATOR PICKLER FETCH| F_STR2LIST |       | |            | |

------------------------------------------------------------------------------------------------

加了cardinality(tab,5)自动走CBO优化器了,优化器把表的基数看成5,前面的binging in list查询基数默认为8168的时候走的是hash join,现在有了cardinality,测试如下:

DINGJUN123> SELECT *
2 FROM t
3 WHERE object_name IN
4 (SELECT /*+cardinality(tab,5)*/ column_value
5 FROM TABLE( CAST( f_str2list(:str) ASvarchar2_tt )) tab
6         );

已选择2行。

执行计划:

----------------------------------------------------------
Plan hash value: 4129437246
------------------------------------------------------------------------------------------------

| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time

------------------------------------------------------------------------------------------------

| 0 | SELECT STATEMENT                     | | 6 | 528 | 36   (3)| 00:00:0

| 1 | TABLE ACCESS BY INDEX ROWID         | T | 1 | 86 | 2   (0)| 00:00:0

| 2 | NESTED LOOPS | | 6 | 528 | 36   (3)| 00:00:0

| 3 | SORT UNIQUE                       | | | | |

| 4 | COLLECTION ITERATOR PICKLER FETCH| F_STR2LIST | | | |

|* 5 | INDEX RANGE SCAN                | IDX_T | 1 | | 1   (0)| 00:00:0

------------------------------------------------------------------------------------------------

Predicate Information (identified by operation id):
---------------------------------------------------
5 - access("OBJECT_NAME"=VALUE(KOKBF$))


统计信息:


----------------------------------------------------------
590  recursive calls
0  db block gets
  149  consistent gets
14  physical reads
0  redo size
1257  bytes sent via SQL*Net to client
384  bytes received via SQL*Net from client
2  SQL*Net roundtrips to/from client
6  sorts (memory)
0  sorts (disk)
2  rows processed


看下第2次软解析的统计信息:


----------------------------------------------------------
0  recursive calls
0  db block gets
  7  consistent gets
0  physical reads
0  redo size
1257  bytes sent via SQL*Net to client
384  bytes received via SQL*Net from client
2  SQL*Net roundtrips to/from client
1  sorts (memory)
0  sorts (disk)
2  rows processed


第1次逻辑读为149,比前面hash join的软解析逻辑读还要少,而且第2次逻辑读为7,则平均每行逻辑读为3.5,效率很好。现在计划走nested loop了,而且对表t也走了索引


总  结




对于前台传入带分隔符的动态字符串作为条件拼凑SQL,需要考虑到绑定变量问题,ORACLE可采用多种方法避免硬解析,常用的就是采用集合函数解决,需要注意的是集合函数默认cardinality:8168导致SQL不能走NESTED LOOPS和索引的问题。

本 文 原 创 来 源:IT那活儿微信公众号(上海新炬王翦团队)


文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/129691.html

相关文章

  • Android开源架构

    摘要:音乐团队分享数据绑定运行机制分析一个项目搞定所有主流架构单元测试一个项目搞定所有主流架构系列的第二个项目。代码开源,展示了的用法,以及如何使用进行测试,还有用框架对的进行单元测试。 Android 常用三方框架的学习 Android 常用三方框架的学习 likfe/eventbus3-intellij-plugin AS 最新可用 eventbus3 插件,欢迎品尝 简单的 MVP 模...

    sutaking 评论0 收藏0
  • Node.js 中遇到含空格 URL 的神奇“Bug”——小范围深入 HTTP 协议

    摘要:开始重现客户端指令其实这次请求的一些猫腻很容易就能发现在中有空格。而在函数中,做的主要事情就是来解析数据包,在解析完成后执行一下回调函数。具体的一些回调函数就不细讲了,有兴趣的童鞋可自行翻阅。如代码片段所示,前文中所对应的函数就是了。 本文首发于知乎专栏蚂蚁金服体验科技。 首先声明,我在Bug字眼上加了引号,自然是为了说明它并非一个真 Bug。 问题抛出 昨天有个童鞋在看后台监控的时候...

    edgardeng 评论0 收藏0
  • Vue render深入窥探之谜

    摘要:仅对于组件,用于监听原生事件,而不是组件内部使用触发的事件。注意,你无法对中的赋值,因为已经自动为你进行了同步。 简介 在使用Vue进行开发的时候,大多数情况下都是使用template进行开发,使用template简单、方便、快捷,可是有时候需要特殊的场景使用template就不是很适合。因此为了很好使用render函数,我决定深入窥探一下。各位看官如果觉得下面写的有不正确之处还望看官...

    Amos 评论0 收藏0
  • 从原理层面掌握@ModelAttribute的使用(核心原理篇)【一起学Spring MVC】

    摘要:虽然它不是必须,但是它是个很好的辅助官方解释首先看看官方的对它怎么说它将方法参数方法返回值绑定到的里面。解析注解标注的方法参数,并处理标注的方法返回值。 每篇一句 我们应该做一个:胸中有蓝图,脚底有计划的人 前言 Spring MVC提供的基于注释的编程模型,极大的简化了web应用的开发,我们都是受益者。比如我们在@RestController标注的Controller控制器组件上用@...

    wdzgege 评论0 收藏0
  • Scala类型推导

    摘要:提供了类型推导来解决这个问题。函数式语言里比较经典的类型推导的方法是,并且它是在里首先使用的。的类型推导有一点点不同,不过思想上是一致的推导所有的约束条件,然后统一到一个类型上。而推导器是所有类型推导器的基础。 Scala类型推导 之剑 2016.5.1 00:38:12 类型系统 什么是静态类型?为什么它们很有用? 根据Picrce的说法:类型系统是一个可以根据代码段计算出来的值对...

    SQC 评论0 收藏0

发表评论

0条评论

IT那活儿

|高级讲师

TA的文章

阅读更多
最新活动
阅读需要支付1元查看
<