[20241227]字符串转换成列表问题.txt

[20241227]字符串转换成列表问题.txt

–//开发经常会写sql语句,经常会出现in (‘111122′,’1111113’..,’2222111′)之类的情况,一般语句in里面内容经常变化,导致无法使

–//用绑定变量。

–//实际上以前例子,通过建立type,然后建立函数将拼接的字符串转换为数字或者字符串列表。

–//前几天有人问及这方面问题,当时建议到网上检索str2numlist,str2varlist就能找到相关例子,照抄就可以了,然后写代码先拼接

–//字符串,这样就可以实现绑定变量。

–//实际上网上的例子没有使用pipe row,感觉这样能节约一些内存,自己修改看看,顺便解答一些问题:

1.环境:

SCOTT@book01p> @ver2

==============================

PORT_STRING                   : x86_64/Linux 2.4.xx

VERSION                       : 21.0.0.0.0

BANNER                        : Oracle Database 21c Enterprise Edition Release 21.0.0.0.0 – Production

BANNER_FULL                   : Oracle Database 21c Enterprise Edition Release 21.0.0.0.0 – Production

Version 21.3.0.0.0

BANNER_LEGACY                 : Oracle Database 21c Enterprise Edition Release 21.0.0.0.0 – Production

CON_ID                        : 0

PL/SQL procedure successfully completed.

2.修改为pipe row:

CREATE OR REPLACE TYPE numtabletype AS TABLE OF NUMBER

/

CREATE OR REPLACE FUNCTION str2numlist (p_string IN VARCHAR2)

   RETURN numtabletype

   PIPELINED

AS

   v_str    LONG :=  p_string || ‘,’;

   v_n      PLS_INTEGER;

   v_index  PLS_INTEGER := 1;

BEGIN

   LOOP

      v_n := TO_NUMBER (INSTR (v_str, ‘,’,v_index));

      EXIT WHEN (NVL (v_n, 0) = 0);

      PIPE ROW ( LTRIM (RTRIM (SUBSTR(p_string, v_index, v_n – v_index))));

      v_index := v_n + 1;

   END LOOP;

   RETURN;

END;

/

CREATE OR REPLACE TYPE vartabletype AS TABLE OF VARCHAR2 (4000)

/

–//感觉不需要定义4000长度,100就可以了。

CREATE OR REPLACE FUNCTION str2varlist (p_string IN VARCHAR2)

   RETURN vartabletype

   PIPELINED

AS

   v_str    LONG :=  p_string || ‘,’;

   v_n      PLS_INTEGER;

   v_index  PLS_INTEGER := 1;

BEGIN

   LOOP

      v_n := TO_NUMBER (INSTR (v_str, ‘,’,v_index));

      EXIT WHEN (NVL (v_n, 0) = 0);

      PIPE ROW ( LTRIM (RTRIM (SUBSTR(p_string, v_index, v_n – v_index))));

      v_index := v_n + 1;

   END LOOP;

   RETURN;

END;

/

3.测试看看:

SCOTT@book01p> @ sl all

alter session set statistics_level = all;

Session altered.

SCOTT@book01p> select * from STR2numlist(‘10,30’) ;

COLUMN_VALUE

————

          10

          30

SCOTT@book01p> select column_value c10 from STR2varlist(‘10,30’) ;

C10

———-

10

30

–//OK.

–//执行计划如下:

SCOTT@book01p> @ dpc ” ” ”

PLAN_TABLE_OUTPUT

————————————-

SQL_ID  ffk198p3px8qx, child number 0

————————————-

select column_value c10 from STR2varlist(‘10,30’)

Plan hash value: 3286354863

———————————————————————————————————————————

| Id  | Operation                         | Name        | Starts | E-Rows |E-Bytes| Cost (%CPU)| E-Time   | A-Rows |   A-Time   |

———————————————————————————————————————————

|   0 | SELECT STATEMENT                  |             |      1 |        |       |    29 (100)|          |      2 |00:00:00.01 |

|   1 |  COLLECTION ITERATOR PICKLER FETCH| STR2VARLIST |      1 |   8168 | 16336 |    29   (0)| 00:00:01 |      2 |00:00:00.01 |

———————————————————————————————————————————

Query Block Name / Object Alias (identified by operation id):

————————————————————-

   1 – SEL$F5BB74E1 / “KOKBF$0″@”SEL$2”

–//注意一些细节,这样返回行数,oracle缺省返回行数是8168,这样可能导致采用这种方式的sql语句趋向不使用索引甚至连接顺序发

–//生了改变。

SCOTT@book01p> variable s varchar2(4000);

SCOTT@book01p> exec :s := ‘10,30’;

PL/SQL procedure successfully completed.

SCOTT@book01p> select * from dept where deptno in (select * from STR2numlist(:s) );

    DEPTNO DNAME                          LOC

———- —————————— ————-

        10 ACCOUNTING                     NEW YORK

        30 SALES                          CHICAGO

SCOTT@book01p> @ dpc ” ” ”

PLAN_TABLE_OUTPUT

————————————-

SQL_ID  6pw75tbqncf8q, child number 0

————————————-

select * from dept where deptno in (select * from STR2numlist(:s) )

Plan hash value: 1925696018

———————————————————————————————————————————————————————–

| Id  | Operation                          | Name        | Starts | E-Rows |E-Bytes| Cost (%CPU)| E-Time   | A-Rows |   A-Time   | Buffers |  OMem |  1Mem | Used-Mem |

———————————————————————————————————————————————————————–

|   0 | SELECT STATEMENT                   |             |      1 |        |       |    32 (100)|          |      2 |00:00:00.01 |       6 |       |       |          |

|*  1 |  HASH JOIN SEMI                    |             |      1 |      1 |    22 |    32   (0)| 00:00:01 |      2 |00:00:00.01 |       6 |  1399K|  1399K| 1045K (0)|

|   2 |   TABLE ACCESS FULL                | DEPT        |      1 |      4 |    80 |     3   (0)| 00:00:01 |      4 |00:00:00.01 |       6 |       |       |          |

|   3 |   COLLECTION ITERATOR PICKLER FETCH| STR2NUMLIST |      1 |   8168 | 16336 |    29   (0)| 00:00:01 |      2 |00:00:00.01 |       0 |       |       |          |

———————————————————————————————————————————————————————–

Query Block Name / Object Alias (identified by operation id):

————————————————————-

   1 – SEL$09D7319C

   2 – SEL$09D7319C / “DEPT”@”SEL$1”

   3 – SEL$09D7319C / “KOKBF$0″@”SEL$3”

Predicate Information (identified by operation id):

—————————————————

   1 – access(“DEPTNO”=VALUE(KOKBF$))

27 rows selected.

–//执行计划选择全部扫描,连接顺序先dept,然后是STR2NUMLIST。

–//当然对于大表也许问题不大,再看下面的例子:

SCOTT@book01p> create table t as select * from all_objects;

Table created.

SCOTT@book01p> create index i_t_objecy_name on t(object_name);

Index created.

SCOTT@book01p> exec :s := ‘DEPT,EMP, OBJ$’;

SCOTT@book01p> select object_id,object_name from t where object_name in (select * from STR2varlist(:s) );

 OBJECT_ID OBJECT_NAME

———- ——————————

        18 OBJ$

     76193 EMP

     76191 DEPT

SCOTT@book01p> @ dpc ” ” ”

PLAN_TABLE_OUTPUT

————————————-

SQL_ID  2ndn1kd52kg01, child number 0

————————————-

select object_id,object_name from t where object_name in (select * from

STR2varlist(:s) )

Plan hash value: 3517368828

———————————————————————————————————————————————————————–

| Id  | Operation                          | Name        | Starts | E-Rows |E-Bytes| Cost (%CPU)| E-Time   | A-Rows |   A-Time   | Buffers |  OMem |  1Mem | Used-Mem |

———————————————————————————————————————————————————————–

|   0 | SELECT STATEMENT                   |             |      1 |        |       |   442 (100)|          |      3 |00:00:00.05 |    1483 |       |       |          |

|*  1 |  HASH JOIN RIGHT SEMI              |             |      1 |      1 |    43 |   442   (1)| 00:00:01 |      3 |00:00:00.05 |    1483 |  2171K|  2171K|  786K (0)|

|   2 |   COLLECTION ITERATOR PICKLER FETCH| STR2VARLIST |      1 |   8168 | 16336 |    29   (0)| 00:00:01 |      3 |00:00:00.01 |       0 |       |       |          |

|   3 |   TABLE ACCESS FULL                | T           |      1 |  70025 |  2803K|   412   (1)| 00:00:01 |  70025 |00:00:00.02 |    1483 |       |       |          |

———————————————————————————————————————————————————————–

Query Block Name / Object Alias (identified by operation id):

————————————————————-

   1 – SEL$09D7319C

   2 – SEL$09D7319C / “KOKBF$0″@”SEL$3”

   3 – SEL$09D7319C / “T”@”SEL$1”

Predicate Information (identified by operation id):

—————————————————

   1 – access(“OBJECT_NAME”=VALUE(KOKBF$))

–//还发现一个细节,看不见绑定变量值。连接顺序先STR2VARLIST,然后是T表,但是执行计划没有使用索引。

SCOTT@book01p> @ modtab t numblks 40000

input argument list : owner.table_name table_stat_attribute value

table_stat_attribute = numrows numblks avgrlen

@desc_proc sys dbms_stats set_table_stats

exec dbms_stats.set_table_stats(‘SCOTT’,’T’,numblks=>40000,NO_INVALIDATE=>false,force=>true);

press enter to continue …

PL/SQL procedure successfully completed.

–//修改表t数据块40000。

SCOTT@book01p> select object_id,object_name from t where object_name in (select * from STR2varlist(:s) );

 OBJECT_ID OBJECT_NAME

———- ——————————

     76191 DEPT

     76193 EMP

        18 OBJ$

SCOTT@book01p> @ dpc ” ” ”

PLAN_TABLE_OUTPUT

————————————-

SQL_ID  2ndn1kd52kg01, child number 1

————————————-

select object_id,object_name from t where object_name in (select * from

STR2varlist(:s) )

Plan hash value: 2618245243

—————————————————————————————————————————————————————————–

| Id  | Operation                            | Name            | Starts | E-Rows |E-Bytes| Cost (%CPU)| E-Time   | A-Rows |   A-Time   | Buffers |  OMem |  1Mem | Used-Mem |

—————————————————————————————————————————————————————————–

|   0 | SELECT STATEMENT                     |                 |      1 |        |       |   796 (100)|          |      3 |00:00:00.01 |      12 |       |       |          |

|   1 |  NESTED LOOPS                        |                 |      1 |    306 | 13158 |   796   (1)| 00:00:01 |      3 |00:00:00.01 |      12 |       |       |          |

|   2 |   NESTED LOOPS                       |                 |      1 |    306 | 13158 |   796   (1)| 00:00:01 |      3 |00:00:00.01 |       9 |       |       |          |

|   3 |    SORT UNIQUE                       |                 |      1 |   8168 | 16336 |    29   (0)| 00:00:01 |      3 |00:00:00.01 |       0 |  2048 |  2048 | 2048  (0)|

|   4 |     COLLECTION ITERATOR PICKLER FETCH| STR2VARLIST     |      1 |   8168 | 16336 |    29   (0)| 00:00:01 |      3 |00:00:00.01 |       0 |       |       |          |

|*  5 |    INDEX RANGE SCAN                  | I_T_OBJECY_NAME |      3 |      1 |       |     2   (0)| 00:00:01 |      3 |00:00:00.01 |       9 |       |       |          |

|   6 |   TABLE ACCESS BY INDEX ROWID        | T               |      3 |      1 |    41 |     3   (0)| 00:00:01 |      3 |00:00:00.01 |       3 |       |       |          |

—————————————————————————————————————————————————————————–

Query Block Name / Object Alias (identified by operation id):

————————————————————-

   1 – SEL$09D7319C

   4 – SEL$09D7319C / “KOKBF$0″@”SEL$3”

   5 – SEL$09D7319C / “T”@”SEL$1”

   6 – SEL$09D7319C / “T”@”SEL$1”

Predicate Information (identified by operation id):

—————————————————

   5 – access(“OBJECT_NAME”=VALUE(KOKBF$))

32 rows selected.

–//当然还有许多方法改变执行计划,这样至少不再出现大量文字变量相关语句,当然不是没有缺点,传入的字符串最大长度4000,如果

–//大于4000,这样方式也不行。

–//还原表统计信息:

SCOTT@book01p> exec dbms_stats.gather_table_stats(‘SCOTT’, ‘T’, estimate_percent => NULL, method_opt=>’FOR TABLE FOR ALL COLUMNS SIZE REPEAT’, cascade=>true, no_invalidate=>false)

PL/SQL procedure successfully completed.

SCOTT@book01p> select /*+ CARDINALITY(“KOKBF$0″@”SEL$3” 10)  */ object_id,object_name from t where object_name in (select * from STR2varlist(:s) );

 OBJECT_ID OBJECT_NAME

———- ——————————

     76191 DEPT

     76193 EMP

        18 OBJ$

SCOTT@book01p> @ dpc ” ” ”

PLAN_TABLE_OUTPUT

————————————-

SQL_ID  60d5pcpx8qr9h, child number 0

————————————-

select /*+ CARDINALITY(“KOKBF$0″@”SEL$3” 10)  */ object_id,object_name

from t where object_name in (select * from STR2varlist(:s) )

Plan hash value: 2618245243

—————————————————————————————————————————————————————————–

| Id  | Operation                            | Name            | Starts | E-Rows |E-Bytes| Cost (%CPU)| E-Time   | A-Rows |   A-Time   | Buffers |  OMem |  1Mem | Used-Mem |

—————————————————————————————————————————————————————————–

|   0 | SELECT STATEMENT                     |                 |      1 |        |       |    45 (100)|          |      3 |00:00:00.01 |      12 |       |       |          |

|   1 |  NESTED LOOPS                        |                 |      1 |     12 |   516 |    45   (3)| 00:00:01 |      3 |00:00:00.01 |      12 |       |       |          |

|   2 |   NESTED LOOPS                       |                 |      1 |     12 |   516 |    45   (3)| 00:00:01 |      3 |00:00:00.01 |       9 |       |       |          |

|   3 |    SORT UNIQUE                       |                 |      1 |     10 |    20 |    29   (0)| 00:00:01 |      3 |00:00:00.01 |       0 |  2048 |  2048 | 2048  (0)|

|   4 |     COLLECTION ITERATOR PICKLER FETCH| STR2VARLIST     |      1 |     10 |    20 |    29   (0)| 00:00:01 |      3 |00:00:00.01 |       0 |       |       |          |

|*  5 |    INDEX RANGE SCAN                  | I_T_OBJECY_NAME |      3 |      1 |       |     2   (0)| 00:00:01 |      3 |00:00:00.01 |       9 |       |       |          |

|   6 |   TABLE ACCESS BY INDEX ROWID        | T               |      3 |      1 |    41 |     3   (0)| 00:00:01 |      3 |00:00:00.01 |       3 |       |       |          |

—————————————————————————————————————————————————————————–

Query Block Name / Object Alias (identified by operation id):

————————————————————-

   1 – SEL$09D7319C

   4 – SEL$09D7319C / “KOKBF$0″@”SEL$3”

   5 – SEL$09D7319C / “T”@”SEL$1”

   6 – SEL$09D7319C / “T”@”SEL$1”

Predicate Information (identified by operation id):

—————————————————

   5 – access(“OBJECT_NAME”=VALUE(KOKBF$))

32 rows selected.

–//通过CARDINALITY提示,设定返回10行,也可以控制执行计划。

4.其他:

–//前面没有抓到绑定变量的值,开始以为是隐含参数_cursor_bind_capture_area_size值,缺省400,测试还是无法抓取到绑定变量值。

SYS@book> @ hidez _cursor_bind_capture_area_size

SYS@book> @ pr

==============================

NUM                           : 4287

N_HEX                         :  10BF

NAME                          : _cursor_bind_capture_area_size

DESCRIPTION                   : maximum size of the cursor bind capture area

DEFAULT_VALUE                 : TRUE

SESSION_VALUE                 : 400

SYSTEM_VALUE                  : 400

ISSES_MODIFIABLE              : FALSE

ISSYS_MODIFIABLE              : IMMEDIATE

PL/SQL procedure successfully completed.

–//除了前面介绍的方法,实际上还有其他的例子,收集整理如下:

–//使用xmltable。

SCOTT@book01p> variable s varchar2(2000);

SCOTT@book01p> exec :s := ‘10,20’;

PL/SQL procedure successfully completed.

SCOTT@book01p> select * from dept where deptno in  (select (column_value).getnumberval() from xmltable(:s));

    DEPTNO DNAME                          LOC

———- —————————— ————-

        10 ACCOUNTING                     NEW YORK

        20 RESEARCH                       DALLAS

SCOTT@book01p> exec :s :=  ‘”SALES”,”RESEARCH”‘;

PL/SQL procedure successfully completed.

–//注意这种方式,字符串里面有双引号

SCOTT@book01p> select * from dept where dname in  (select (column_value).getstringval() from xmltable(:s));

    DEPTNO DNAME                          LOC

———- —————————— ————-

        30 SALES                          CHICAGO

        20 RESEARCH                       DALLAS

–//换成单引号。

SCOTT@book01p>  exec :s := ”’SALES”,”RESEARCH”’;

PL/SQL procedure successfully completed.

SCOTT@book01p> select * from dept where dname in  (select (column_value).getstringval() from xmltable(:s));

    DEPTNO DNAME                          LOC

———- —————————— ————-

        30 SALES                          CHICAGO

        20 RESEARCH                       DALLAS

SCOTT@book01p> exec :s := q'[‘SALES’,’RESEARCH’]’;

PL/SQL procedure successfully completed.

SCOTT@book01p> select * from dept where dname in  (select (column_value).getstringval() from xmltable(:s));

    DEPTNO DNAME                          LOC

———- —————————— ————-

        30 SALES                          CHICAGO

        20 RESEARCH                       DALLAS

        

SCOTT@book01p> exec :s := ‘SALES,RESEARCH’;

PL/SQL procedure successfully completed.

SCOTT@book01p> select * from dept where dname in  (select (column_value).getstringval() from xmltable(:s));

select * from dept where dname in  (select (column_value).getstringval() from xmltable(:s))

*

ERROR at line 1:

ORA-19112: error raised during evaluation:

XVM-01002: [XPDY0002] Dynamic context component ‘context item’ has no value

$ oerr ora 19112

19112, 00000, “error raised during evaluation: %s”

// *Cause:  The error function was called during evaluation of the XQuery expression.

// *Action: Check the detailed error message for the possible causes.

–//没有单双引号的字符串报错。

SCOTT@book01p> exec :s := ‘SALES,RESEARCH’;

PL/SQL procedure successfully completed.

SCOTT@book01p> exec :s :=  ‘”‘||replace(:s,’,’,'”,”‘)||'”‘

PL/SQL procedure successfully completed.

SCOTT@book01p> select * from dept where dname in  (select (column_value).getstringval() from xmltable(:s));

    DEPTNO DNAME                          LOC

———- —————————— ————-

        30 SALES                          CHICAGO

        20 RESEARCH                       DALLAS

–//奇怪改写如下不行。

SCOTT@book01p> exec :s := ‘SALES,RESEARCH’;

PL/SQL procedure successfully completed.

SCOTT@book01p> select * from dept where dname in  (select (column_value).getstringval() from xmltable( ‘”‘||replace(:s,’,’,'”,”‘)||'”‘));

select * from dept where dname in  (select (column_value).getstringval() from xmltable( ‘”‘||replace(:s,’,’,'”,”‘)||'”‘))

                                                                                           *

ERROR at line 1:

ORA-02000: missing COLUMNS keyword

–//使用正则表达式REGEXP_SUBSTR+CONNECT BY:

SCOTT@book01p> exec :s := ‘10,20’;

PL/SQL procedure successfully completed.

SCOTT@book01p> SELECT * FROM dept WHERE deptno IN ( SELECT TO_number (REGEXP_SUBSTR ( :s ,'[^,]+’ ,1 ,LEVEL)) FROM DUAL CONNECT BY REGEXP_SUBSTR ( :s ,'[^,]+’,1 ,LEVEL) IS NOT NULL);

    DEPTNO DNAME                          LOC

———- —————————— ————-

        10 ACCOUNTING                     NEW YORK

        20 RESEARCH                       DALLAS

SCOTT@book01p> exec :s := ‘SALES,RESEARCH’;

PL/SQL procedure successfully completed.

SCOTT@book01p> SELECT * FROM dept WHERE dname IN ( SELECT TO_char (REGEXP_SUBSTR ( :s ,'[^,]+’ ,1 ,LEVEL)) FROM DUAL CONNECT BY REGEXP_SUBSTR ( :s ,'[^,]+’,1 ,LEVEL) IS NOT NULL);

    DEPTNO DNAME                          LOC

———- —————————— ————-

        20 RESEARCH                       DALLAS

        30 SALES                          CHICAGO

–//注意使用正则表达式的方式,如果变量很多,该方式消耗CPU资源有点多。

–//也许还有其他的方法,我不知道。

来源链接:https://www.cnblogs.com/lfree/p/18637977

© 版权声明
THE END
支持一下吧
点赞12 分享
评论 抢沙发
头像
请文明发言!
提交
头像

昵称

取消
昵称表情代码快捷回复

    暂无评论内容