Six years ago(!) I did an Office Hours session where I demonstrated a little routine that would let you generate more efficient INSERTs by converting a standard INSERT-VALUES into a multi-table insert (INSERT ALL) in order to reduce the number of network trips.

Of course that is a moot point with 26ai because we have more options at our disposal with INSERT. For example, you can have multiple rows in a single VALUES clause.

For full details on that, check out this video

But to my knowledge, none of the tools available out there have caught up with the feature advancements in 26ai, so I figured I should spruce up my little demo from 6 years ago so allow you generate 26ai-style INSERTS. Here is the fresh version:


SQL> create or replace
  2  function as_insert26(p_query varchar2, p_batch int default 10, p_use_set_clause boolean default false) return sys.odcivarchar2list pipelined as
  3      l_theCursor     integer default dbms_sql.open_cursor;
  4      l_columnValue   varchar2(4000);
  5      l_status        integer;
  6      l_descTbl       dbms_sql.desc_tab;
  7      l_colCnt        number;
  8      n               number := 0;
  9      l_pad           int := 0;
 10
 11      l_tname varchar2(2000) := lower(translate(p_query,chr(10)||chr(13)||',','   '));
 12      l_collist varchar2(32000);
 13      l_colval varchar2(32000);
 14      l_dml varchar2(32000);
 15
 16      l_nls sys.odcivarchar2list := sys.odcivarchar2list();
 17
 18      function format_col(l_existing varchar2, p_value varchar2, p_colname varchar2) return varchar2 is
 19      begin
 20        if p_use_set_clause then
 21          return '  '||rpad(p_colname,l_pad)||' = '||p_value||',';
 22        else
 23          return l_existing||p_value||',';
 24        end if;
 25      end;
 26
 27  begin
 28     l_tname := substr(l_tname,instr(l_tname,' from')+6)||' ';
 29     l_tname := substr(l_tname,1,instr(l_tname,' '));
 30
 31     if l_tname is null then l_tname := '@@TABLE@@'; end if;
 32
 33     select value
 34     bulk collect into l_nls
 35     from v$nls_parameters
 36     where parameter in (
 37        'NLS_DATE_FORMAT',
 38        'NLS_TIMESTAMP_FORMAT',
 39        'NLS_TIMESTAMP_TZ_FORMAT')
 40     order by parameter;
 41
 42      execute immediate 'alter session set nls_date_format=''yyyy-mm-dd hh24:mi:ss'' ';
 43      execute immediate 'alter session set nls_timestamp_format=''yyyy-mm-dd hh24:mi:ssff'' ';
 44      execute immediate 'alter session set nls_timestamp_tz_format=''yyyy-mm-dd hh24:mi:ssff tzr'' ';
 45
 46      dbms_sql.parse(  l_theCursor,  p_query, dbms_sql.native );
 47      dbms_sql.describe_columns( l_theCursor, l_colCnt, l_descTbl );
 48
 49      for i in 1 .. l_colCnt loop
 50          dbms_sql.define_column(l_theCursor, i, l_columnValue, 4000);
 51          l_collist := l_collist || l_descTbl(i).col_name||',';
 52          l_pad := greatest(l_pad, length(l_descTbl(i).col_name));
 53      end loop;
 54      l_collist := 'into '||l_tname||'('||rtrim(l_collist,',')||')';
 55      l_pad := l_pad + 2;
 56
 57      l_status := dbms_sql.execute(l_theCursor);
 58
 59
 60      pipe row('alter session set cursor_sharing = force;');
 61      while ( dbms_sql.fetch_rows(l_theCursor) > 0 ) loop
 62         n := n + 1;
 63
 64         if p_use_set_clause then
 65           pipe row('insert into '||l_tname);
 66           pipe row('set');
 67         else
 68           if mod(n,p_batch) = 1 or p_batch = 1 then
 69              if n > 1 then
 70                l_colval := rtrim(l_colval,',')||';';
 71                pipe row (l_colval);
 72              end if;
 73              pipe row('insert '||l_collist||' values');
 74           else
 75               if nvl(length(l_colval),0) > 0 then
 76                 pipe row (l_colval);
 77               end if;
 78           end if;
 79           l_colval := '  (';
 80         end if;
 81
 82          for i in 1 .. l_colCnt loop
 83              dbms_sql.column_value( l_theCursor, i, l_columnValue );
 84              if l_columnValue is null then
 85                l_colval := format_col(l_colval , 'null',l_descTbl(i).col_name);
 86              elsif l_descTbl(i).col_type in (1,8,9,96,112) then
 87                l_colval := format_col(l_colval , 'q''{'||l_columnValue ||'}''' ,l_descTbl(i).col_name);
 88              elsif l_descTbl(i).col_type in (2,100,101) then
 89                l_colval := format_col(l_colval , l_columnValue,l_descTbl(i).col_name);
 90              elsif l_descTbl(i).col_type in (12) then
 91                l_colval := format_col(l_colval , 'to_date('''||l_columnValue||''',''yyyy-mm-dd hh24:mi:ss'')',l_descTbl(i).col_name);
 92              elsif l_descTbl(i).col_type in (180) then
 93                l_colval := format_col(l_colval , 'to_timestamp('''||l_columnValue||''',''yyyy-mm-dd hh24:mi:ssff'')',l_descTbl(i).col_name);
 94              elsif l_descTbl(i).col_type in (181) then
 95                l_colval := format_col(l_colval ,'to_timestamp_tz('''||l_columnValue||''',''yyyy-mm-dd hh24:mi:ssff tzr'')',l_descTbl(i).col_name);
 96              elsif l_descTbl(i).col_type in (231) then
 97                l_colval := format_col(l_colval , 'to_timestamp('''||l_columnValue||''',''yyyy-mm-dd hh24:mi:ssff'')' ,l_descTbl(i).col_name);
 98              elsif l_descTbl(i).col_type in (182) then
 99                l_colval := format_col(l_colval , 'to_yminterval('''||l_columnValue||''')' ,l_descTbl(i).col_name);
100              elsif l_descTbl(i).col_type in (183) then
101                l_colval := format_col(l_colval ,'to_dsinterval('''||l_columnValue||''')' ,l_descTbl(i).col_name);
102              end if;
103              if p_use_set_clause then
104                if i < l_colCnt then
105                  pipe row (l_colval);
106                else
107                  pipe row (rtrim(l_colval,',')||';');
108                end if;
109              end if;
110          end loop;
111          if not p_use_set_clause then
112            l_colval := rtrim(l_colval,',')||'),';
113          end if;
114      end loop;
115      if n = 0 then
116        pipe row( 'No data found ');
117      else
118        if not p_use_set_clause then
119          l_colval := rtrim(l_colval,',')||';';
120          pipe row (l_colval);
121        end if;
122      end if;
123      pipe row('alter session set cursor_sharing = exact;');
124
125      execute immediate 'alter session set nls_date_format='''||l_nls(1)||''' ';
126      execute immediate 'alter session set nls_timestamp_format='''||l_nls(2)||''' ';
127      execute immediate 'alter session set nls_timestamp_tz_format='''||l_nls(3)||''' ';
128      return;
129  end;
130  /

Function created.

Now you can choose the number of rows per VALUE clause, or if network trips is not an issue, why not have beautiful self-documenting INSERTs with the SET clause variant.


SQL> select * from as_insert26('select * from scott.emp',5);

COLUMN_VALUE
---------------------------------------------------------------------------------------------------------------
alter session set cursor_sharing = force;
insert into scott.emp (EMPNO,ENAME,JOB,MGR,HIREDATE,SAL,COMM,DEPTNO) values
  (7369,q'{SMITH}',q'{CLERK}',7902,to_date('1980-12-17 00:00:00','yyyy-mm-dd hh24:mi:ss'),800,null,20),
  (7499,q'{ALLEN}',q'{SALESMAN}',7698,to_date('1981-02-20 00:00:00','yyyy-mm-dd hh24:mi:ss'),1600,300,30),
  (7521,q'{WARD}',q'{SALESMAN}',7698,to_date('1981-02-22 00:00:00','yyyy-mm-dd hh24:mi:ss'),1250,500,30),
  (7566,q'{JONES}',q'{MANAGER}',7839,to_date('1981-04-02 00:00:00','yyyy-mm-dd hh24:mi:ss'),2975,null,20),
  (7654,q'{MARTIN}',q'{SALESMAN}',7698,to_date('1981-09-28 00:00:00','yyyy-mm-dd hh24:mi:ss'),1250,1400,30);
insert into scott.emp (EMPNO,ENAME,JOB,MGR,HIREDATE,SAL,COMM,DEPTNO) values
  (7698,q'{BLAKE}',q'{MANAGER}',7839,to_date('1981-05-01 00:00:00','yyyy-mm-dd hh24:mi:ss'),2850,null,30),
  (7782,q'{CLARK}',q'{MANAGER}',7839,to_date('1981-06-09 00:00:00','yyyy-mm-dd hh24:mi:ss'),2450,null,10),
  (7788,q'{SCOTT}',q'{ANALYST}',7566,to_date('1982-12-09 00:00:00','yyyy-mm-dd hh24:mi:ss'),3000,null,20),
  (7839,q'{KING}',q'{PRESIDENT}',null,to_date('1981-11-17 00:00:00','yyyy-mm-dd hh24:mi:ss'),5000,null,10),
  (7844,q'{TURNER}',q'{SALESMAN}',7698,to_date('1981-09-08 00:00:00','yyyy-mm-dd hh24:mi:ss'),1500,null,30);
insert into scott.emp (EMPNO,ENAME,JOB,MGR,HIREDATE,SAL,COMM,DEPTNO) values
  (7876,q'{ADAMS}',q'{CLERK}',7788,to_date('1983-01-12 00:00:00','yyyy-mm-dd hh24:mi:ss'),1100,null,20),
  (7900,q'{JAMES}',q'{CLERK}',7698,to_date('1981-12-03 00:00:00','yyyy-mm-dd hh24:mi:ss'),950,null,30),
  (7902,q'{FORD}',q'{ANALYST}',7566,to_date('1981-12-03 00:00:00','yyyy-mm-dd hh24:mi:ss'),3000,null,20),
  (7934,q'{MILLER}',q'{CLERK}',7782,to_date('1982-01-23 00:00:00','yyyy-mm-dd hh24:mi:ss'),1300,null,10);
alter session set cursor_sharing = exact;


SQL> select * from as_insert26('select * from scott.dept',p_use_set_clause=>true);

COLUMN_VALUE
---------------------------------------------------------------------------------------
alter session set cursor_sharing = force;
insert into scott.dept
set
  DEPTNO   = 10,
  DNAME    = q'{ACCOUNTING}',
  LOC      = q'{NEW YORK}';
insert into scott.dept
set
  DEPTNO   = 20,
  DNAME    = q'{RESEARCH}',
  LOC      = q'{DALLAS}';
insert into scott.dept
set
  DEPTNO   = 30,
  DNAME    = q'{SALES}',
  LOC      = q'{CHICAGO}';
insert into scott.dept
set
  DEPTNO   = 40,
  DNAME    = q'{OPERATIONS}',
  LOC      = q'{BOSTON}';
alter session set cursor_sharing = exact;


Enjoy!

Got some thoughts? Leave a comment

This site uses Akismet to reduce spam. Learn how your comment data is processed.

Trending