1

我必须插入表 2 字段(首先是主键(关于文章),第二个是关于它们的大小(这些文章)。

在源环境中,我将表、主键(TK 文章)和大小串联到第二个字段中。但是,我必须在目标表中插入 TK 文章和几个大小的文章。例如,

资源:

ART        SIZE**                                      
1        |  28/30   
2        |  30/32   
3        | Size 10/Size 12/Size 14/Size 14

目标:

ART         Size
1        |   28  
1        |   30                   
2        |   30            
2        |   32             
3        |  Size 10         
3        |  Size 12       
3        |  Size 14      
3        |  Size 16

难点是要知道字段中包含多少个'/'?

我已查询

SELECT ART,
       REGEXP_SUBSTR(SIZE,'[^/]+',1,level)
FROM TABLLE
CONNECT BY REGEXP_SUBSTR(SIZE,'[^/]+',1,level) IS NOT NULL;

选择事务工作并在 46 秒内显示结果。但是 TABLE 有 100 000 行,并且插入事务太长并且不起作用。

有人可以在这一点上帮助我吗?

感谢和问候

4

3 回答 3

1

正则表达式的计算成本非常高。如果需要处理大量行,我个人会使用存储过程 - 流水线表函数:

-- table with 100000 rows
create table Tb_SplitStr(col1, col2) as
  select level
       , 'Size 10/Size 12/Size 14/Size 14/Size 15/Size 16/Size 17'
   from dual
  connect by level <= 100000 
  1. PL/SQL 包:

    create or replace package Split_Pkg as
      type T_StrList is table of varchar2(1000);
      function Str_Split(
         p_str in varchar2,
         p_dlm in varchar2
      ) return T_StrList pipelined;
    end;
    
    create or replace package body Split_Pkg as
      function Str_Split(
         p_str in varchar2,
         p_dlm in varchar2
      ) return T_StrList pipelined
      is
         l_src_str  varchar2(1000) default p_str;
         l_dlm_pos  number;
      begin
         while l_src_str is not null
         loop
            l_dlm_pos := instr(l_src_str, p_dlm);
            case
              when l_dlm_pos = 0
              then pipe row (l_src_str);
                   l_src_str := '';
              else pipe row(substr(l_src_str, 1, l_dlm_pos - 1));
                   l_src_str := substr(l_src_str, l_dlm_pos + 1);
            end case;
         end loop;
         return;
      end; 
    end;
    
  2. 使用正则表达式函数的 SQL 查询:

    with ocrs(ocr) as(
       select level
         from ( select max(regexp_count(col2, '[^/]+')) as mx
                  from tb_splitStr) t
       connect by level <= t.mx
    )
    select count(regexp_substr(s.col2, '[^/]+', 1, o.ocr)) as res
      from tb_splitStr s
       cross join ocrs o
    

结果:

-- SQL with regexp
SQL> with ocrs(ocr) as(
  2    select level
  3     from ( select max(regexp_count(col2, '[^/]+')) as mx
  4              from tb_splitStr) t
  5    connect by level <= t.mx
  6  )
  7  select count(regexp_substr(s.col2, '[^/]+', 1, o.ocr)) as res
  8    from tb_splitStr s
  9     cross join ocrs o
 10  ;

Res
------------------------------
                        700000
Executed in 4.093 seconds

SQL> /

Res
------------------------------
                        700000
Executed in 3.812 seconds



--Query with pipelined table function  
SQL> select count(*)
  2    from Tb_SplitStr s
  3    cross join table(split_pkg.Str_Split(s.col2, '/'))
  4  ;

 COUNT(*)
----------
    700000
Executed in 2.469 seconds

SQL> /

COUNT(*)
----------
    700000
Executed in 2.406 seconds
于 2013-09-13T13:52:32.363 回答
0

我的这篇博文展示了六种不同的技术来处理这个查询。

不同之处在于它处理日期,而您需要处理字符串。您可以通过使用 "regexp_count(size,'/') + 1" 作为迭代停止器和选择列表中的 regexp_substr(size,'[^/]+',1,i) 来解决这个问题。

于 2013-09-13T14:22:52.787 回答
0

使用一些 XML 怎么样?

> set serveroutput on
> drop table test_tab
table TEST_TAB dropped.
> create table test_tab
(
art number,
siz varchar2(100)
)
table TEST_TAB created.
> insert into test_tab values (1, '28/30')
1 rows inserted.
> insert into test_tab values (2, '30/32')
1 rows inserted.
> insert into test_tab values (3, 'Size 10/Size 12/Size 14/Size 14')
1 rows inserted.
> commit
committed.
> drop table test_tab2
table TEST_TAB2 dropped.
> create table test_tab2 as
select * from test_tab where 1=0
table TEST_TAB2 created.
> insert into test_tab2 (art, siz)
select art, extractvalue(x.column_value, 'e')
from test_tab, xmltable ('e' passing xmlparse( content  '<e>' || replace(siz, '/', '</e><e>') || '</e>')) x
8 rows inserted.
> commit
committed.
> select * from test_tab2
       ART SIZ                                                                                                
---------- ----------------------------------------------------------------------------------------------------
         1 28                                                                                                   
         1 30                                                                                                   
         2 30                                                                                                   
         2 32                                                                                                   
         3 Size 10                                                                                              
         3 Size 12                                                                                              
         3 Size 14                                                                                              
         3 Size 14                                                                                              

 8 rows selected 

又来了,但最初有 100,000 行,并显示了时间。插入 400,000 行只用了 2 分钟多一点:

> set serveroutput on
> set timing on
> drop table test_tab
table TEST_TAB dropped.
Elapsed: 00:00:00.055
> create table test_tab
(
art number,
siz varchar2(100)
)
table TEST_TAB created.
Elapsed: 00:00:00.059
> --insert into test_tab values (1, '28/30');
> --insert into test_tab values (2, '30/32');
> --insert into test_tab values (3, 'Size 10/Size 12/Size 14/Size 14');
> insert into test_tab (art, siz)
select level, 'Size 10/Size 12/Size 14/Size 16'
  from dual
  connect by level <= 100000
100,000 rows inserted.
Elapsed: 00:00:00.191
> commit
committed.
Elapsed: 00:00:00.079
> drop table test_tab2
table TEST_TAB2 dropped.
Elapsed: 00:00:00.081
> create table test_tab2 as
select * from test_tab where 1=0
table TEST_TAB2 created.
Elapsed: 00:00:00.076
> -- perform inserts.  This will result in 400,000 rows inserted
> -- note inserts are done conventionally (timing is acceptable)
> insert into test_tab2 (art, siz)
select art, extractvalue(x.column_value, 'e')
from test_tab, xmltable ('e' passing xmlparse( content  '<e>' || replace(siz, '/', '</e><e>') || '</e>')) x
400,000 rows inserted.
Elapsed: 00:02:17.046
> commit
committed.
Elapsed: 00:00:00.094
> -- show some data in target table
> select * from test_tab2
where art = 1
       ART SIZ                                                                                                
---------- ----------------------------------------------------------------------------------------------------
         1 Size 10                                                                                              
         1 Size 12                                                                                              
         1 Size 14                                                                                              
         1 Size 16                                                                                              

Elapsed: 00:00:00.103
于 2013-09-13T15:02:35.840 回答