oracle批量绑定 forall bulk collect用法以及测试案例

2023-11-19

一、如何使用批挷定提高性能(How Do Bulk Binds Improve Performance)
  在PL/SQL 和SQL引擎(engines)中,太多的上下文切换(context switches)会影响性能。这个会发生在当一个循环为集合中的每个元素执行一个单个SQL语句时。而使用批挷定能显著提高性能。下图显示PL/SQL 引擎 和SQL引擎之间的context switches:(PL/SQL引擎执行存过语句仅发送SQL语句到SQL引擎,SQL引擎执行语句后返回数据给PL/SQL引擎)
  PL/SQL引擎发送一次SQL语句给SQL引擎,在SQL引擎中则为范围中每个index数字执行一次SQL语句。
  PL/SQL挷定操作包含以下三类:
  in-bind: When a PL/SQL variable or host variable is stored in the database by an INSERT or UPDATE statement.
  out-bind:When a database value is assigned to a PL/SQL variable or a host variable by the RETURNING clause of an INSERT, UPDATE, or DELETE statement.
  define: When a database value is assigned to a PL/SQL variable or a host variable by a SELECT or FETCH statement.
  在SQL语句中,为PL/SQL变量指定值称为挷定(binding),
  DML语句能传递所有集合元素到一个单个操作中,这过程称为批挷定(bulk binding)。
  如果集合有20个元素,批挷定让你用单个操作等效于执行与20个SELECT,INSERT,   UPDATE或DELETE语句。这个技术通过减少在PL/SQL和SQL引擎(engines)间的上下文切换来提高性能。批挷定包括:
  1.带INSERT, UPDATE, and DELETE语句的批挷定:在FORALL语句中嵌入SQL语句
  2.带SELECT语句的批挷定:在SELECT语句中用BULK COLLECT 语句代替INTO
  下边的例子分别用FOR和FORALL进行数据插入,以显示用批挷定的对性能的提高:

Java代码
  1. SQL> SET SERVEROUTPUT ON  
  2. SQL> CREATE TABLE parts (pnum NUMBER(4 ), pname CHAR( 15 ));  
  3. Table created.  
  4. SQL> DECLARE  
  5. 2  TYPE NumTab IS TABLE OF parts.pnum%TYPE INDEX BY BINARY_INTEGER;  
  6. 3  TYPE NameTab IS TABLE OF parts.pname%TYPE INDEX BY BINARY_INTEGER;  
  7. 4  pnums NumTab;  
  8. 5  Pnames NameTab;  
  9. 6  t1 NUMBER;  
  10. 7  t2 NUMBER;  
  11. 8  t3 NUMBER;  
  12. 9  BEGIN  
  13. 10  FOR i IN  1 .. 500000  LOOP  
  14. 11  pnums(i) := i;  
  15. 12  pnames(i) :=  'Part No.' ||to_char(i);  
  16. 13  END LOOP;  
  17. 14  t1 := dbms_utility.get_time;  
  18. 15   
  19. 16  FOR i IN  1 .. 500000  LOOP  
  20. 17  INSERT INTO parts VALUES(pnums(i),pnames(i));  
  21. 18  END LOOP;  
  22. 19  t2 := dbms_utility.get_time;  
  23. 20   
  24. 21  FORALL i IN  1 .. 500000   
  25. 22  INSERT INTO parts VALUES(pnums(i),pnames(i));  
  26. 23  t3 := dbms_utility.get_time;  
  27. 24   
  28. 25  dbms_output.put_line( 'Execution Time (secs)' );  
  29. 26  dbms_output.put_line( '---------------------' );  
  30. 27  dbms_output.put_line( 'FOR loop: '  || TO_CHAR(t2 - t1));  
  31. 28  dbms_output.put_line( 'FORALL: '  || TO_CHAR(t3 - t2));  
  32. 29  END;  
SQL> SET SERVEROUTPUT ON
SQL> CREATE TABLE parts (pnum NUMBER(4), pname CHAR(15));
Table created.
SQL> DECLARE
2 TYPE NumTab IS TABLE OF parts.pnum%TYPE INDEX BY BINARY_INTEGER;
3 TYPE NameTab IS TABLE OF parts.pname%TYPE INDEX BY BINARY_INTEGER;
4 pnums NumTab;
5 Pnames NameTab;
6 t1 NUMBER;
7 t2 NUMBER;
8 t3 NUMBER;
9 BEGIN
10 FOR i IN 1..500000 LOOP
11 pnums(i) := i;
12 pnames(i) := 'Part No.'||to_char(i);
13 END LOOP;
14 t1 := dbms_utility.get_time;
15
16 FOR i IN 1..500000 LOOP
17 INSERT INTO parts VALUES(pnums(i),pnames(i));
18 END LOOP;
19 t2 := dbms_utility.get_time;
20
21 FORALL i IN 1..500000
22 INSERT INTO parts VALUES(pnums(i),pnames(i));
23 t3 := dbms_utility.get_time;
24
25 dbms_output.put_line('Execution Time (secs)');
26 dbms_output.put_line('---------------------');
27 dbms_output.put_line('FOR loop: ' || TO_CHAR(t2 - t1));
28 dbms_output.put_line('FORALL: ' || TO_CHAR(t3 - t2));
29 END;


SQL> /
Execution Time (secs)
---------------------
FOR loop: 2592
FORALL: 358
PL/SQL procedure successfully completed
  从而可以看出FORALL语句在性能上有显著提高。
  注释:SQL语句能涉及多个集合,然而,性能提高只适用于下标集合(subscripted collections)
二、FORALL 如何影响回滚(How FORALL Affects Rollbacks)
  在FORALL语句中,如果任何SQL语句执行产生未处理的异常(exception),先前执行的所有数据库改变都会被回滚。然而,如果产生的异常被捕获并处理,则回滚改变到一个隐式的保存点,该保存点在每个SQL语句执行前被标记。之前的改变不会被回滚。例如:

Java代码
  1. CREATE TABLE emp2 (deptno NUMBER( 2 ), job VARCHAR2( 15 ));  
  2. INSERT INTO emp2 VALUES(10'Clerk' );  
  3. INSERT INTO emp2 VALUES(10'Clerk' );  
  4. INSERT INTO emp2 VALUES(20'Bookkeeper' ); --  10 - char  job title  
  5. INSERT INTO emp2 VALUES(30'Analyst' );  
  6. INSERT INTO emp2 VALUES(30'Analyst' );  
  7. Comit;  
  8. DECLARE  
  9. TYPE NumList IS TABLE OF NUMBER;  
  10. depts NumList := NumList(102030 );  
  11. BEGIN  
  12. FORALL j IN depts.FIRST..depts.LAST  
  13. UPDATE emp2 SET job = job || ' (temp)'   
  14. WHERE deptno = depts(j);  
  15. -- raises a "value too large"  exception  
  16. EXCEPTION  
  17. WHEN OTHERS THEN  
  18. COMMIT;  
  19. END;  
  20. /  
  21. PL/SQL procedure successfully completed  
  22. SQL> select * from emp2;  
CREATE TABLE emp2 (deptno NUMBER(2), job VARCHAR2(15));
INSERT INTO emp2 VALUES(10, 'Clerk');
INSERT INTO emp2 VALUES(10, 'Clerk');
INSERT INTO emp2 VALUES(20, 'Bookkeeper'); -- 10-char job title
INSERT INTO emp2 VALUES(30, 'Analyst');
INSERT INTO emp2 VALUES(30, 'Analyst');
Comit;
DECLARE
TYPE NumList IS TABLE OF NUMBER;
depts NumList := NumList(10, 20, 30);
BEGIN
FORALL j IN depts.FIRST..depts.LAST
UPDATE emp2 SET job = job || ' (temp)'
WHERE deptno = depts(j);
-- raises a "value too large" exception
EXCEPTION
WHEN OTHERS THEN
COMMIT;
END;
/
PL/SQL procedure successfully completed
SQL> select * from emp2;


DEPTNO JOB
---------- ---------------
10 Clerk temp
10 Clerk temp
20 Bookkeeper
30 Analyst
30 Analyst
  上边的例子SQL引擎执行UPDATE语句3次,指定范围内的每个索引号一次。第一个(depts(10))执行成功,但是第二个(depts(20))执行失败(插入值超过了列长),因此,仅仅第二个执行被回滚。
  当执行任何SQL语句引发异常时,FORALL语句中断(halt)。上边的例子中,执行第二个UPDATE语句引发异常, 因此第三个语句不会执行。
三、用%BULK_ROWCOUNT 属性计算FORALL迭代影响行数
  在进行SQL数据操作语句时,SQL引擎打开一个隐式游标(命名为SQL),该游标的标量属性(scalar attribute)有 %FOUND, %ISOPEN, %NOTFOUND, and %ROWCOUNT。
  FORALL语句除具有上边的标量属性外,还有个复合属性(composite attribute):%BULK_ROWCOUNT,该属性具有索引表(index-by table)语法。它的第i个元素存贮SQL语句(INSERT, UPDATE或DELETE)第i个执行的处理行数。如果第i个执行未影响行,%bulk_rowcount (i),返回0。FORALL与%bulk_rowcount属性使用相同下标。例如:


DECLARE
TYPE NumList IS TABLE OF NUMBER;
depts NumList := NumList(10, 20, 50);
BEGIN
FORALL j IN depts.FIRST..depts.LAST
UPDATE emp SET sal = sal * 1.10 WHERE deptno = depts(j);
-- Did the 3rd UPDATE statement affect any rows?
IF SQL%BULK_ROWCOUNT(3) = 0 THEN ...
END;
  %ROWCOUNT 返回SQL语句所有执行处理总的行数
  %FOUND和 %NOTFOUND仅与SQL语句的最后执行有关,但是,可以使用%BULK_ROWCOUNT推断单个执行的值,如%BULK_ROWCOUNT(i)为0时,%FOUND和%NOTFOUND分别是FALSE和TRUE。
四、用%BULK_EXCEPTIONS属性处理FORALL异常
  在执行FORALL语句期间,PL/SQL提供一个处理异常的机制。该机制使批挷定(bulk-bind)操作能保存异常信息并继续执行。方法是在FORALL语句中增加SAVE EXCEPTIONS关键字。语法为:
  FORALL index IN lower_bound..upper_bound SAVE EXCEPTIONS
    {insert_stmt | update_stmt | delete_stmt}
  执行期间引发的所有异常都被保存游标属性 %BULK_EXCEPTIONS中,它存贮一个集合记录,每记录有两个字段:
  %BULK_EXCEPTIONS(i).ERROR_INDEX:存贮在引发异常期间FORALL语句迭代(重复:iteration)
  %BULK_EXCEPTIONS(i).ERROR_CODE:存贮相应的Oracle错误代码
  %BULK_EXCEPTIONS.COUNT存贮异常的数量。(该属性不是%BULK_EXCEPTIONS集合记录的字段)。如果忽略 SAVE EXCEPTIONS,当引发异常时,FORALL语句停止执行。此时,SQL%BULK_EXCEPTIONS.COUNT 返回1, 且SQL%BULK_EXCEPTIONS只包含一条记录。如果执行期间无异常 SQL%BULK_EXCEPTIONS.COUNT 返回 0.例子:


DECLARE
TYPE NumList IS TABLE OF NUMBER;
num_tab NumList := NumList(10,0,11,12,30,0,20,199,2,0,9,1);
errors NUMBER;
dml_errors EXCEPTION;
PRAGMA exception_init(dml_errors, -24381);
BEGIN
FORALL i IN num_tab.FIRST..num_tab.LAST SAVE EXCEPTIONS
DELETE FROM emp WHERE sal > 500000/num_tab(i);
EXCEPTION
WHEN dml_errors THEN
errors := SQL%BULK_EXCEPTIONS.COUNT;
dbms_output.put_line('Number of errors is ' || errors);
FOR i IN 1..errors LOOP
dbms_output.put_line('Error ' || i || ' occurred during '||
'iteration ' || SQL%BULK_EXCEPTIONS(i).ERROR_INDEX);
dbms_output.put_line('Oracle error is ' ||
SQLERRM(-SQL%BULK_EXCEPTIONS(i).ERROR_CODE));
END LOOP;
END;
该例子中,当i等于2,6,10时,产生异常ZERO_DIVIDE,完成后SQL%BULK_EXCEPTIONS.COUNT为3,其值为(2,1476), (6,1476)和(10,1476),错误输出如下:
  Number of errors is 3
  Error 1 occurred during iteration 2
  Oracle error is ORA-01476: divisor is equal to zero
  Error 2 occurred during iteration 6
  Oracle error is ORA-01476: divisor is equal to zero
  Error 3 occurred during iteration 10
  Oracle error is ORA-01476: divisor is equal to zero
五、用BULK COLLECT子句取回查询结果至集合中
  在返回到PL/SQL引擎之前,关键字BULK COLLECT告诉SQL引擎批挷定输出集合。该关键字能用于SELECT INTO, FETCH INTO和RETURNING INTO语句中。语法如下:
  ... BULK COLLECT INTO collection_name[, collection_name] ...


示例1:
DECLARE
TYPE NumTab IS TABLE OF emp.empno%TYPE;
TYPE NameTab IS TABLE OF emp.ename%TYPE;
enums NumTab; -- no need to initialize
names NameTab;
BEGIN
SELECT empno, ename BULK COLLECT INTO enums, names FROM emp;
...
END;
示例2:
CREATE TYPE Coords AS OBJECT (x NUMBER, y NUMBER);
CREATE TABLE grid (num NUMBER, loc Coords);
INSERT INTO grid VALUES(10, Coords(1,2));
INSERT INTO grid VALUES(20, Coords(3,4));
DECLARE
TYPE CoordsTab IS TABLE OF Coords;
pairs CoordsTab;
BEGIN
SELECT loc BULK COLLECT INTO pairs FROM grid;
-- now pairs contains (1,2) and (3,4)
END;
示例3:
DECLARE
TYPE SalList IS TABLE OF emp.sal%TYPE;
sals SalList;
BEGIN
SELECT sal BULK COLLECT INTO sals FROM emp
WHERE ROWNUM <= 100;
...
END;
示例4:Examples of Bulk Fetching from a Cursor:
DECLARE
TYPE NameList IS TABLE OF emp.ename%TYPE;
TYPE SalList IS TABLE OF emp.sal%TYPE;
CURSOR c1 IS SELECT ename, sal FROM emp WHERE sal > 1000;
names NameList;
sals SalList;
BEGIN
OPEN c1;
FETCH c1 BULK COLLECT INTO names, sals; --可返回到一个或多个集合
END;
示例5:Examples of Bulk Fetching from a Cursor:
DECLARE
TYPE DeptRecTab IS TABLE OF dept%ROWTYPE;
dept_recs DeptRecTab;
CURSOR c1 IS
SELECT deptno, dname, loc FROM dept WHERE deptno > 10;
BEGIN
OPEN c1;
FETCH c1 BULK COLLECT INTO dept_recs; --返回到一个记录(records)集合
END;
六、用LIMIT 子句限制批取出操作行
  LIMIT子句可限制从数据库中取出的行数。该子句仅能用于批(非标量 not scalar)FETCH语句.语法为:
  FETCH ... BULK COLLECT INTO ... [LIMIT rows];
  rows可以是文字的(literal),变量(variable)或表达式(expression),但必须返回一个数字。否 则,PL/SQL引发预定义异常VALUE_ERROR,如果为负数,PL/SQ引发INVALID_NUMBER。如果需要,PL/SQL四舍五入 (round)为最近的整数.
  下例中,在每个循环迭代中,FETCH语句返回到索引表empnos中,先前的值会被覆盖。


DECLARE
TYPE NumTab IS TABLE OF NUMBER INDEX BY BINARY_INTEGER;
CURSOR c1 IS SELECT empno FROM emp;
empnos NumTab;
rows NATURAL := 10;
BEGIN
OPEN c1;
LOOP
/**//* The following statement fetches 10 rows (or less). */
FETCH c1 BULK COLLECT INTO empnos LIMIT rows;
EXIT WHEN c1%NOTFOUND;
...
END LOOP;
CLOSE c1;
END;
七、用RETURNING INTO子句取回DML结果至集合
  你能在INSERT、UPDATE或DELETE语句的RETURNING INTO子句中使用BULK COLLECT子句。(注意,此处是没有SELECT语句的)


DECLARE
TYPE NumList IS TABLE OF emp.empno%TYPE;
enums NumList;
BEGIN
DELETE FROM emp WHERE deptno = 20
RETURNING empno BULK COLLECT INTO enums;
-- if there were five employees in department 20,
-- then enums contains five employee numbers
END;
八、BULK COLLECT上的限制
  1.You cannot bulk collect into an associative array that has a string type for the key. .
  2.BULK COLLECT语句只能用于服务器端(server-side),而非客户端
  3.在BULK COLLECT INTO语句中的所有目标必须是集合,如下例:


DECLARE
TYPE NameList IS TABLE OF emp.ename%TYPE;
names NameList;
salary emp.sal%TYPE;
BEGIN
SELECT ename, sal BULK COLLECT INTO names, salary -- illegal target
FROM emp WHERE ROWNUM < 50;
...
END;.
  4.复合目标(如对象)不能用于RETURNING INTO子句中.
  5.当需要隐式数据类型转换时,多复合目标(如对象集合)不能用于bulk collect into子句。
九、同时使用FORALL 和BULK COLLECT
  Using FORALL and BULK COLLECT Together
  你能使FORALL语句与BULK COLLECT结合,如下例:如果集合depts有3个元素,每个元素导致5行被删除,则语句完成时,集合enums有15个元素:
  FORALL j IN depts.FIRST..depts.LAST
  DELETE FROM emp WHERE empno = depts(j)
  RETURNING empno BULK COLLECT INTO enums;
  注意:不能在FORALL语句中使用SELECT ... BULK COLLECT语句。
十、Using Host Arrays with Bulk Binds
  客户端程序能用匿名PL/SQL块批挷定输入和输出数组(arrays)。实际上,这是与服务器端传递集合最有效的方式。
  Host arrays are declared in a host environment such as an OCI or Pro*C program and must be prefixed with a colon to distinguish them from PL/SQL collections. In the example below, an input host array is used in a DELETE statement. At run time, the anonymous PL/SQL block is sent to the database server for execution.
  DECLARE
  ...
  BEGIN
  -- assume that values were assigned to the host array
  -- and host variables in the host environment
  FORALL i IN :lower..:upper
  DELETE FROM emp WHERE deptno = :depts(i);
  ...
  END;
  限制:以下限制将应用于FORALL语句:
  1. 在FORALL循环中,UPDATE语句中的SET子句和WHERE子句中不能指向同一个集合,你可能需要获得另一个集合副本,以在WHERE子句指向新的名称。
  2. INSERT,UPDATE或DELETE语句必须至少涉及一个集合。如在LOOP插入一组常量的FORALL语句将引发异常。( FORALL的INSERT之类的语句一定要用in-bind 方式,如:


SQL> BEGIN
2 FORALL i IN 1..100
3 INSERT INTO parts VALUES(i,i);
4 END;
5 /
ORA-06550: 第 3 行, 第 5 列:
PLS-00435: 没有 BULK In-BIND 的 DML 语句在 FORALL 内不能使用
ORA-06550: 第 2 行, 第 12 列:
PL/SQL: Statement ignored
  3. 指定范围内的所有集合元素必须存在,如果一元素丢失或删除,你将收到一个错误,如:


DECLARE
TYPE NumList IS TABLE OF NUMBER;
depts NumList := NumList(10, 20, 30, 40);
BEGIN
depts.DELETE(3); -- delete third element
FORALL i IN depts.FIRST..depts.LAST
DELETE FROM emp WHERE deptno = depts(i); -- causes an error
END;
  4. 下例显示,复合值的输入集合不能被分解和跳跃数据库列


CREATE TABLE coords (x NUMBER, y NUMBER);
CREATE TYPE Pair AS OBJECT (m NUMBER, n NUMBER);
DECLARE
TYPE PairTab IS TABLE OF Pair;
pairs PairTab := PairTab(Pair(1,2), Pair(3,4), Pair(5,6));
TYPE NumTab IS TABLE OF NUMBER;
nums NumTab := NumTab(1, 2, 3);
BEGIN
/**//* The following statement fails. */
FORALL i IN 1..3
UPDATE coords SET (x, y) = pairs(i)
WHERE x = nums(i);
END;
The workaround is to decompose the composite values manually:
DECLARE
TYPE PairTab IS TABLE OF Pair;
pairs PairTab := PairTab(Pair(1,2), Pair(3,4), Pair(5,6));
TYPE NumTab IS TABLE OF NUMBER;
nums NumTab := NumTab(1, 2, 3);
BEGIN
/**//* The following statement succeeds. */
FORALL i in 1..3
UPDATE coords SET (x, y) = (pairs(i).m, pairs(i).n)
WHERE x = nums(i);
END;
  5. 集合下标不能是表达式,示例:
  FORALL j IN mgrs.FIRST..mgrs.LAST
  DELETE FROM emp WHERE mgr = mgrs(j+1); -- invalid subscript
  6. 游标属性%BULK_ROWCOUNT不能分配给其它集合,同样,它也不能作为参数传递到子程序。

 

 

 

 

 

 

 

 

******************************************************************************************************************

Version 11.1
Array Processing
Note: Without the bulk bind, PL/SQL sends a SQL statement to the SQL engine for each record that is inserted, updated, or deleted leading to context switches that hurt performance.
 
BULK COLLECT

BULK COLLECT Syntax
FETCH BULK COLLECT <cursor_name> BULK COLLECT INTO <collection_name>
LIMIT <numeric_expression>;

or

FETCH BULK COLLECT <cursor_name> BULK COLLECT INTO <array_name>
LIMIT <numeric_expression>;
set timing on

DECLARE
 CURSOR a_cur IS
 SELECT program_id
 FROM airplanes;
BEGIN
  FOR cur_rec IN a_cur LOOP
    NULL;
  END LOOP;
END;
/

DECLARE
 CURSOR a_cur IS
 SELECT program_id
 FROM airplanes;

 TYPE myarray IS TABLE OF a_cur%ROWTYPE;
 cur_array myarray;
BEGIN
  OPEN a_cur;
  LOOP
    FETCH a_cur BULK COLLECT INTO cur_array LIMIT 100 ;
    EXIT WHEN a_cur%NOTFOUND;
  END LOOP;
  CLOSE a_cur;
END;
/

DECLARE
 CURSOR a_cur IS
 SELECT program_id
 FROM airplanes;

 TYPE myarray IS TABLE OF a_cur%ROWTYPE;
 cur_array myarray;
BEGIN
  OPEN a_cur;
  LOOP
    FETCH a_cur BULK COLLECT INTO cur_array LIMIT 500 ;
    EXIT WHEN a_cur%NOTFOUND;
  END LOOP;
  CLOSE a_cur;
END;
/

DECLARE
 CURSOR a_cur IS
 SELECT program_id
 FROM airplanes;

 TYPE myarray IS TABLE OF a_cur%ROWTYPE;
 cur_array myarray;
BEGIN
  OPEN a_cur;
  LOOP
    FETCH a_cur BULK COLLECT INTO cur_array LIMIT 1000 ;
    EXIT WHEN a_cur%NOTFOUND;
  END LOOP;
  CLOSE a_cur;
END;
/

-- try with a LIMIT clause of 2500, 5000, and 10000. What do you see?
 
FORALL

FORALL Syntax
FORALL <index_name> IN <lower_boundary> .. <upper_boundary>
<sql_statement>
SAVE EXCEPTIONS;

FORALL <index_name> IN INDICES OF <collection>
[BETWEEN <lower_boundary> AND <upper_boundary>]
<sql_statement>
SAVE EXCEPTIONS;

FORALL <index_name> IN INDICES OF <collection>
VALUES OF <index_collection>
<sql_statement>
SAVE EXCEPTIONS;

FORALL Insert
CREATE TABLE servers2 AS
SELECT *
FROM servers
WHERE 1=2;

DECLARE
 CURSOR s_cur IS
 SELECT *
 FROM servers;

 TYPE fetch_array IS TABLE OF s_cur%ROWTYPE;
 s_array fetch_array;
BEGIN
  OPEN s_cur;
  LOOP
    FETCH s_cur BULK COLLECT INTO s_array LIMIT 1000;

    FORALL i IN 1..s_array.COUNT
    INSERT
INTO servers2 VALUES s_array(i);

    EXIT WHEN s_cur%NOTFOUND;
  END LOOP;
  CLOSE s_cur;
  COMMIT;
END;
/

FORALL Update
SELECT DISTINCT srvr_id
FROM servers2
ORDER BY 1;

DECLARE
 TYPE myarray IS TABLE OF servers2.srvr_id%TYPE
 INDEX BY BINARY_INTEGER;

 d_array myarray;
BEGIN
  d_array(1) := 608;
  d_array(2) := 610;
  d_array(3) := 612;

  FORALL i IN d_array.FIRST .. d_array.LAST
  UPDATE servers2
  SET srvr_id = 0
  WHERE srvr_id = d_array(i) ;

  COMMIT;
END;
/

SELECT srvr_id
FROM servers2
WHERE srvr_id = 0;

FORALL Delete
set serveroutput on

DECLARE
 TYPE myarray IS TABLE OF servers2.srvr_id%TYPE
 INDEX BY BINARY_INTEGER;

 d_array myarray;
BEGIN
  d_array(1) := 614;
  d_array(2) := 615;
  d_array(3) := 616;

  FORALL i IN d_array.FIRST .. d_array.LAST
  DELETE servers2
  WHERE srvr_id = d_array(i) ;

  COMMIT;

  FOR i IN d_array.FIRST .. d_array.LAST LOOP
    dbms_output.put_line('Iteration #' || i || ' deleted ' ||
    SQL%BULK_ROWCOUNT (i) || ' rows.');
  END LOOP;
END;
/

SELECT srvr_id
FROM servers2
WHERE srvr_id IN (614, 615, 616);
 
Performance Demos

Performance Comparison
CREATE TABLE t1 (pnum INTEGER, pname VARCHAR2(15));
CREATE TABLE t2 AS SELECT * FROM t1;

CREATE OR REPLACE PROCEDURE perf_compare(iterations PLS_INTEGER) IS

 TYPE NumTab IS TABLE OF t1.pnum%TYPE INDEX BY PLS_INTEGER;
 TYPE NameTab IS TABLE OF t1.pname%TYPE INDEX BY PLS_INTEGER;
 pnums  NumTab;
 pnames NameTab;

 a INTEGER;
 b INTEGER;
 c INTEGER;
BEGIN
  FOR j IN 1..iterations LOOP -- load index-by tables
    pnums(j) := j;
    pnames(j) := 'Part No. ' || TO_CHAR(j);
  END LOOP;

  a := dbms_utility.get_time;

  FOR i IN 1..iterations LOOP -- use FOR loop
    INSERT INTO t1 VALUES (pnums(i), pnames(i));
  END LOOP;

  b := dbms_utility.get_time;

  FORALL i IN 1 .. iterations -- use FORALL statement
  INSERT INTO t2 VALUES (pnums(i), pnames(i));

  c := dbms_utility.get_time;

  dbms_output.put_line('Execution Time (secs)');
  dbms_output.put_line('---------------------');
  dbms_output.put_line('FOR loop: ' || TO_CHAR((b - a)/100));
  dbms_output.put_line('FORALL: ' || TO_CHAR((c - b)/100));
  COMMIT;
END perf_compare;
/

set serveroutput on

exec perf_compare(500);
exec perf_compare(5000);
exec perf_compare(50000);
 
Bulk Collection Demo Table CREATE TABLE parent (
part_num  NUMBER,
part_name VARCHAR2(15));

CREATE TABLE child AS
SELECT *
FROM parent;

Create And Load Demo Data
DECLARE
 j PLS_INTEGER := 1;
 k parent.part_name%TYPE := 'Transducer';
BEGIN
  FOR i IN 1 .. 200000
  LOOP
    SELECT DECODE(k, 'Transducer', 'Rectifier',
    'Rectifier', 'Capacitor',
    'Capacitor', 'Knob',
    'Knob', 'Chassis',
    'Chassis', 'Transducer')
    INTO k
    FROM dual;

    INSERT INTO parent VALUES (j+i, k);
  END LOOP;
  COMMIT;
END;
/

SELECT COUNT(*) FROM parent;
SELECT COUNT(*) FROM child;

Slow Way
CREATE OR REPLACE PROCEDURE slow_way IS

BEGIN
  FOR r IN (SELECT * FROM parent)
  LOOP
    -- modify record values
    r.part_num := r.part_num * 10 ;
    -- store results
    INSERT INTO child
    VALUES
    (r.part_num, r.part_name);
  END LOOP;
  COMMIT;
END slow_way;
/

set timing on

exec slow_way -- 07.71

Fast Way 1

Fetch into user defined array
CREATE OR REPLACE PROCEDURE fast_way IS

TYPE myarray IS TABLE OF parent%ROWTYPE;
l_data myarray;

CURSOR r IS
SELECT part_num , part_name
FROM parent;

BEGIN
  OPEN r;
  LOOP
    FETCH r BULK COLLECT INTO l_data LIMIT 1000;

    FOR j IN 1 .. l_data.COUNT
    LOOP
      l_data(j).part_num := l_data(j).part_num * 10;
    END LOOP;

    FORALL i IN 1..l_data.COUNT
    INSERT INTO child VALUES l_data(i);

    EXIT WHEN r%NOTFOUND;
  END LOOP;
  COMMIT;
  CLOSE r;
END fast_way;
/

set timing on

exec fast_way -- 00.50

set timing off

SELECT 7.71/0.50 FROM dual;

Fast Way 2

Fetch into user defined PL/SQL table
CREATE OR REPLACE PROCEDURE fast_way IS

TYPE PartNum IS TABLE OF parent.part_num%TYPE
INDEX BY BINARY_INTEGER;
pnum_t PartNum;

TYPE PartName IS TABLE OF parent.part_name%TYPE
INDEX BY BINARY_INTEGER;
pnam_t PartName;

BEGIN
  SELECT part_num, part_name
  BULK COLLECT INTO pnum_t, pnam_t
  FROM parent;

  FOR i IN pnum_t.FIRST .. pnum_t.LAST
  LOOP
    pnum_t(i) := pnum_t(i) * 10;
  END LOOP;


  FORALL i IN pnum_t.FIRST .. pnum_t.LAST
  INSERT INTO child
  (part_num, part_name)
  VALUES
  (pnum_t(i), pnam_t(i));
  COMMIT;
END fast_way;
/

set timing on

exec fast_way -- 0.62

Fast Way 3

Fetch into DBMS_SQL defined array
CREATE OR REPLACE PROCEDURE fast_way IS

TYPE parent_rec IS RECORD (
part_num   dbms_sql.number_table,
part_name  dbms_sql.varchar2_table);


p_rec parent_rec;

CURSOR c IS
SELECT part_num, part_name
FROM parent;

l_done BOOLEAN;

BEGIN
  OPEN c;
  LOOP
    FETCH c BULK COLLECT INTO p_rec.part_num , p_rec.part_name
    LIMIT 500;
    l_done := c%NOTFOUND;

    FOR i IN 1 .. p_rec.part_num.COUNT
    LOOP
      p_rec.part_num(i) := p_rec.part_num(i) * 10;
    END LOOP;


    FORALL i IN 1 .. p_rec.part_num.COUNT
    INSERT INTO child
    (part_num, part_name)
    VALUES
    (p_rec.part_num(i), p_rec.part_name(i));

    EXIT WHEN (l_done);
  END LOOP;
  COMMIT;
  CLOSE c;
END fast_way;
/

set timing on

exec fast_way -- 0.51

Fast Way 4

Affect of triggers on performance of cursor loops vs. array processing
TRUNCATE TABLE child;

set timing on

exec slow_way;
exec fast_way;

set timing off

TRUNCATE TABLE child;

CREATE OR REPLACE TRIGGER bi_child
BEFORE INSERT
ON child
FOR EACH ROW
BEGIN
  NULL;
END bi_child;
/

set timing on

exec slow_way;
-- Elapsed: 00:05:54.36

exec fast_way;
-- Elapsed: 00:00:01.96

Fast Way 5

Insert into multiple tables
TRUNCATE TABLE child;

RENAME child TO child1;

CREATE TABLE child2 AS
SELECT * FROM child1;
 
CREATE OR REPLACE PROCEDURE fast_way IS

TYPE myarray IS TABLE OF parent%ROWTYPE;
l_data myarray;

CURSOR r IS
SELECT part_num, part_name
FROM parent;

BEGIN
  OPEN r;
  LOOP
    FETCH r BULK COLLECT INTO l_data LIMIT 1000;

    FOR j IN 1 .. l_data.COUNT LOOP
      l_data(j).part_num := l_data(j).part_num * 10;
    END LOOP;

    FORALL i IN 1..l_data.COUNT
    INSERT INTO child1 VALUES l_data(i);


    FORALL i IN 1..l_data.COUNT
    INSERT INTO child2 VALUES l_data(i);


    EXIT WHEN r%NOTFOUND;
  END LOOP;
  COMMIT;
  CLOSE r;
END fast_way;
/

set timing on

exec fast_way

 
Partial Collections

Part of Collection Demo
CREATE TABLE test (
deptno  NUMBER(3,0),
empname VARCHAR2(20));

INSERT INTO test VALUES (100, 'Morgan');
INSERT INTO test VALUES (200, 'Allen');
INSERT INTO test VALUES (101, 'Lofstrom');
INSERT INTO test VALUES (102, 'Havemeyer');
INSERT INTO test VALUES (202, 'Norgaard');
INSERT INTO test VALUES (201, 'Lewis');
INSERT INTO test VALUES (103, 'Scott');
INSERT INTO test VALUES (104, 'Foote');
INSERT INTO test VALUES (105, 'Townsend');
INSERT INTO test VALUES (106, 'Abedrabbo');
COMMIT;

SELECT * FROM test;

CREATE OR REPLACE PROCEDURE collection_part IS
 TYPE NumList IS VARRAY(10) OF NUMBER;
 depts NumList := NumList(100,200,101,102,202,201,103,104,105,106);
BEGIN
  FORALL j IN 4..7 -- use only part of varray
  DELETE FROM test WHERE deptno = depts(j) ;
  COMMIT;
END collection_part;
/

SELECT * FROM test;
 
Sparse Collection
Note: A sparse collection is one from which elements have been deleted.

Sparse Collection Demo using IN INDICES OF
ALTER TABLE child
ADD CONSTRAINT uc_child_part_num
UNIQUE (part_num)
USING INDEX;

DECLARE
 TYPE typ_part_name IS TABLE OF parent%ROWTYPE;
 v_part  typ_part_name;
BEGIN
  SELECT *
  BULK COLLECT INTO v_part
  FROM parent;

  FOR rec IN 1 .. v_part.LAST()
  LOOP
    IF v_part(rec).part_name != 'Rectifier' THEN
      v_part.delete(rec);
    END IF;
  END LOOP;

  FORALL i IN 1 .. v_part.COUNT
  INSERT INTO child
  VALUES
  v_part(i);

  COMMIT;
END;
/

DECLARE
  TYPE typ_part_name IS TABLE OF parent%ROWTYPE;
  v_part  typ_part_name;
BEGIN
  SELECT *
  BULK COLLECT INTO v_part
  FROM parent;

  FOR rec IN 1 .. v_part.LAST
  LOOP
    IF v_part(rec).part_name != 'Rectifier' THEN
      v_part.delete(rec);
    END IF;
  END LOOP;

  FORALL idx IN INDICES OF v_part
  INSERT INTO child
  VALUES
  v_part(idx);

  COMMIT;
END;
/

SELECT COUNT(*) FROM parent;

SELECT COUNT(*) FROM child;

Using INDICES OF and VALUES OF with Non-Consecutive Index Values
CREATE TABLE valid_orders (
cust_name VARCHAR2(32),
amount NUMBER(10,2));

CREATE TABLE big_orders AS
SELECT * FROM valid_orders WHERE 1 = 0;

CREATE TABLE rejected_orders AS
SELECT * FROM valid_orders WHERE 1 = 0;

DECLARE
  -- collections to hold a set of customer names and amounts 
 SUBTYPE cust_name IS valid_orders.cust_name%TYPE;
 TYPE cust_typ IS TABLe OF cust_name;
 cust_tab cust_typ;

 SUBTYPE order_amount IS valid_orders.amount%TYPE;
 TYPE amount_typ IS TABLE OF NUMBER;
 amount_tab amount_typ;

  -- collections to point into the CUST_TAB collection.
 TYPE index_pointer_t IS TABLE OF PLS_INTEGER;

 big_order_tab index_pointer_t := index_pointer_t();

 rejected_order_tab index_pointer_t := index_pointer_t();

PROCEDURE setup_data IS
BEGIN
  -- Set up sample order data, with some invalid and 'big' orders
  cust_tab := cust_typ('Company1', 'Company2', 'Company3',
  'Company4', 'Company5');

  amount_tab := amount_typ(5000.01, 0, 150.25, 4000.00, NULL);
END setup_data;

BEGIN
  setup_data;

  dbms_output.put_line('--- Original order data ---');
  FOR i IN 1..cust_tab.LAST
  LOOP
    dbms_output.put_line('Cust#' || i || ', '|| cust_tab(i) ||
    ': $'||amount_tab(i));
  END LOOP;

  -- Delete invalid orders (where amount is null or 0)
  FOR i IN 1..cust_tab.LAST
  LOOP
    IF amount_tab(i) is null or amount_tab(i) = 0 THEN
      cust_tab.delete(i);
      amount_tab.delete(i);
    END IF;
  END LOOP;

  dbms_output.put_line('---Data with deleted invalid orders---');

  FOR i IN 1..cust_tab.LAST LOOP
    IF cust_tab.EXISTS(i) THEN
      dbms_output.put_line('Cust#' || i || ', ' || cust_tab(i) ||
      ': $'||amount_tab(i));
    END IF;
  END LOOP;

  -- Since the subscripts of our collections are not consecutive,
  -- we use use FORRALL...INDICES OF to iterate the subscripts

  FORALL i IN INDICES OF cust_tab
  INSERT INTO valid_orders
  (cust_name, amount)
  VALUES
  (cust_tab(i), amount_tab(i));

  -- Now let's process the order data differently extracting 
  --  2 subsets and storing each subset in a different table.

  setup_data; -- Reinitialize the CUST_TAB and AMOUNT_TAB collections

  FOR i IN cust_tab.FIRST .. cust_tab.LAST
  LOOP
    IF amount_tab(i) IS NULL OR amount_tab(i) = 0 THEN
      -- add a new element to the collection
      rejected_order_tab.EXTEND ;
      -- record original collection subscript
      rejected_order_tab(rejected_order_tab.LAST) := i;
    END IF;

    IF amount_tab(i) > 2000 THEN
      -- Add a new element to the collection
      big_order_tab.EXTEND ;
      -- record original collection subscript
      big_order_tab(big_order_tab.LAST) := i;
    END IF;
  END LOOP;

  -- run one DML statement on one subset of elements,
  -- and another DML statement on a different subset.

  FORALL i IN VALUES OF rejected_order_tab
  INSERT INTO rejected_orders VALUES (cust_tab(i), amount_tab(i));

  FORALL i IN VALUES OF big_order_tab
  INSERT INTO big_orders VALUES (cust_tab(i), amount_tab(i));

  COMMIT;
END;
/

-- Verify that the correct order details were stored
SELECT cust_name "Customer", amount "Valid order amount"
FROM valid_orders;

SELECT cust_name "Customer", amount "Big order amount"
FROM big_orders;

SELECT cust_name "Customer", amount "Rejected order amount"
FROM rejected_orders;
 
Exception Handling

Bulk Collection Exception Handling

CREATE TABLE tmp_target AS SELECT table_name, num_rows
FROM all_tables
WHERE 1=2;

ALTER TABLE tmp_target
ADD CONSTRAINT cc_num_rows
CHECK (num_rows > 0);


CREATE OR REPLACE PROCEDURE forall_errors IS

TYPE myarray IS TABLE OF tmp_target%ROWTYPE;
l_data myarray;

CURSOR c IS
SELECT table_name, num_rows
FROM all_tables;

errors PLS_INTEGER;

dml_errors EXCEPTION;
PRAGMA EXCEPTION_INIT(dml_errors, -24381);

BEGIN
  OPEN c;
  LOOP
    FETCH c BULK COLLECT INTO l_data LIMIT 100;

    -- SAVE EXCEPTIONS means don't stop if some DELETES fail
    FORALL i IN 1..l_data.COUNT SAVE EXCEPTIONS
    INSERT INTO tmp_target VALUES l_data(i);

    -- If any errors occurred during the FORALL SAVE EXCEPTIONS,
    -- a single exception is raised when the statement completes.
    EXIT WHEN c%NOTFOUND;
  END LOOP;
EXCEPTION
  WHEN dml_errors THEN
    errors := SQL%BULK_EXCEPTIONS.COUNT ;
    dbms_output.put_line('Number of DELETE statements that
    failed: ' || errors);

    FOR i IN 1 .. errors
    LOOP
      dbms_output.put_line('Error #' || i || ' at '|| 'iteration
      #' || SQL%BULK_EXCEPTIONS (i).ERROR_INDEX );
      dbms_output.put_line('Error message is ' ||
      SQLERRM(-SQL%BULK_EXCEPTIONS (i).ERROR_CODE ));
    END LOOP;
  WHEN OTHERS THEN
    RAISE;
END forall_errors;
/

SQL> exec forall_errors;

SQL>  SELECT * FROM tmp_target;


Exception Handling Demo
CREATE OR REPLACE PROCEDURE array_exceptions IS

-- cursor for processing load_errors
CURSOR le_cur IS
SELECT *
FROM load_errors
FOR UPDATE;


TYPE myarray IS TABLE OF test%ROWTYPE;
l_data myarray;

CURSOR c IS
SELECT sub_date, cust_account_id, carrier_id, ticket_id, upd_date
FROM stage
FOR UPDATE SKIP LOCKED;

 errors PLS_INTEGER;

 cai   test.cust_account_id%TYPE;
 cid   test.carrier_id%TYPE;
 ecode NUMBER;
 iud   stage.upd_date%TYPE;
 sd    test.sub_date%TYPE;
 tid   test.ticket_id%TYPE;
 upd   test.upd_date%TYPE;
BEGIN
  OPEN c;
  LOOP
    FETCH c BULK COLLECT INTO l_data LIMIT 50000;

    FORALL i IN 1..l_data.COUNT SAVE EXCEPTIONS
    INSERT INTO test VALUES l_data(i);

    EXIT WHEN c%NOTFOUND;
  END LOOP;
  COMMIT; -- Exits here when no exceptions are raised
EXCEPTION
  WHEN OTHERS THEN
    -- get the number of errors in the exception array
    errors := SQL%BULK_EXCEPTIONS.COUNT ;

    -- insert all exceptions into the load_errors table
    FOR j IN 1 .. errors LOOP
      ecode := SQL%BULK_EXCEPTIONS(j).ERROR_CODE;
      sd := TRUNC(l_data(SQL%BULK_EXCEPTIONS(j).ERROR_INDEX).sub_date);
      cai := l_data(SQL%BULK_EXCEPTIONS(j).ERROR_INDEX).cust_account_id;
      cid := l_data(SQL%BULK_EXCEPTIONS(j).ERROR_INDEX).carrier_id;
      tid := l_data(SQL%BULK_EXCEPTIONS(j).ERROR_INDEX).ticket_id;

      INSERT INTO load_errors
      (error_code, sub_date, cust_account_id, carrier_id, ticket_id)
      VALUES
      (ecode, sd, cai, cid, tid);
    END LOOP;


    -- for each record in load_errors process those that can
    -- be handled and delete them after successful handling

    FOR le_rec IN le_cur LOOP
      IF le_rec.error_code = 1 THEN
        SELECT upd_date
        INTO iud
        FROM test
        WHERE cust_account_id = le_rec.cust_account_id
        AND carrier_id = le_rec.carrier_id
        AND ticket_id = le_rec.ticket_id;

        IF iud IS NULL THEN
          RAISE;
        ELSIF iud < le_rec.upd_date THEN
          UPDATE test
          SET upd_date = le_rec.upd_date
          WHERE sub_date = le_rec.sub_date
          AND cust_account_id = le_rec.cust_account_id
          AND carrier_id = le_rec.carrier_id
          AND ticket_id = le_rec.ticket_id;
        ELSE
          RAISE;
        END IF;
      END IF;
    END LOOP;

    COMMIT; -- Exits here when any existing found.
END array_exceptions;
/
 
Native Dynamic SQL

Dynamic SQL Inside a FORALL Statement
CREATE TABLE tmp_target AS SELECT rownum ID, table_name, num_rows
FROM all_tables
WHERE rownum < 101;

DECLARE
 TYPE NumList IS TABLE OF NUMBER;
 rownos NumList;

 TYPE NameList IS TABLE OF VARCHAR2(30);
 tnames NameList;
BEGIN
  rownos := NumList(2,4,6,8,16);

  FORALL i IN 1..5
  EXECUTE IMMEDIATE 'UPDATE tmp_target SET id = id * 1.1
  WHERE id = :1
  RETURNING table_name INTO :2'
  USING rownos(i) RETURNING BULK COLLECT INTO tnames;

  FOR j IN 1..5
  LOOP
    dbms_output.put_line(tnames(j));
  END LOOP;
END;
/
 
Array Of Records Demo

You cannot bulk collect into an ARRAY OF RECORDS. You can into a RECORD OF ARRAYS..... 

This demo intentionally generates an error. Familiarize yourself with the error and message so you will recognize it
CREATE OR REPLACE TYPE uw_sel_row AS OBJECT (
part_num NUMBER, part_name VARCHAR2(15));
/

CREATE OR REPLACE PROCEDURE wrong_way IS
 TYPE uw_sel_tab IS TABLE OF uw_sel_row;
 uw_selection uw_sel_tab;
BEGIN
  SELECT uw_sel_row(part_num, part_name)
  BULK COLLECT INTO uw_selection
  FROM parent;

  FOR i IN 1..uw_selection.count
  LOOP
    uw_selection(i).part_num := uw_selection(i).part_num * 10;
  END LOOP;

  FORALL i IN 1..uw_selection.COUNT
  INSERT INTO child
  VALUES
  (uw_selection(i).part_num, uw_selection(i).part_name);
  COMMIT;
END wrong_way;
/

sho err

drop type uw_sel_row;

CREATE OR REPLACE PROCEDURE right_way IS

 TYPE uw_sel_row IS TABLE OF parent%ROWTYPE;
  uw_selection uw_sel_row;
BEGIN
  SELECT part_num, part_name
  BULK COLLECT INTO uw_selection
  FROM parent;

  FOR i IN 1..uw_selection.count
  LOOP
    uw_selection(i).part_num := uw_selection(i).part_num * 10;
  END LOOP;

  FORALL i IN 1..uw_selection.COUNT
  INSERT INTO child VALUES uw_selection(i);
  COMMIT;
END right_way;
/
 
Bulk Collect Into DBMS_SQL Data Types

Bulk Collect with DBMS_SQL Data Types
CREATE TABLE t AS
SELECT *
FROM all_objects
WHERE 1=0;

CREATE OR REPLACE PROCEDURE nrows_at_a_time(p_array_size PLS_INTEGER) IS
 l_owner          dbms_sql.VARCHAR2_table ;
 l_object_name    dbms_sql.VARCHAR2_table;
 l_subobject_name dbms_sql.VARCHAR2_table;
 l_object_id      dbms_sql.NUMBER_table ;
 l_data_object_id dbms_sql.NUMBER_table;
 l_object_type    dbms_sql.VARCHAR2_table;
 l_created        dbms_sql.DATE_table ;
 l_last_ddl_time  dbms_sql.DATE_table;
 l_timestamp      dbms_sql.VARCHAR2_table;
 l_status         dbms_sql.VARCHAR2_table;
 l_temporary      dbms_sql.VARCHAR2_table;
 l_generated      dbms_sql.VARCHAR2_table;
 l_secondary      dbms_sql.VARCHAR2_table;

 CURSOR c IS
 SELECT *
 FROM all_objects
 WHERE SUBSTR(object_name,1,1) BETWEEN 'A' AND 'W';
BEGIN
  OPEN c;
  LOOP
    FETCH c BULK COLLECT INTO
    l_owner, l_object_name, l_subobject_name, l_object_id,
    l_data_object_id, l_object_type, l_created,
    l_last_ddl_time, l_timestamp, l_status, l_temporary,
    l_generated, l_secondary

    LIMIT p_array_size ;

    FORALL i in 1 .. l_owner.COUNT
      INSERT INTO t
      (owner, object_name, subobject_name, object_id,
       data_object_id, object_type, created, last_ddl_time,
       timestamp, status, temporary, generated, secondary)
      VALUES
      (l_owner(i), l_object_name(i), l_subobject_name(i),
       l_object_id(i), l_data_object_id(i),
       l_object_type(i), l_created(i), l_last_ddl_time(i),
       l_timestamp(i), l_status(i), l_temporary(i),
       l_generated(i), l_secondary(i)
);
    EXIT WHEN c%NOTFOUND;
  END LOOP;
  COMMIT;
  CLOSE c;
END nrows_at_a_time;
/
 
 

 

 

 

 

 

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

oracle批量绑定 forall bulk collect用法以及测试案例 的相关文章

  • ORA-00972 标识符别名列名太长

    我有一个查询 例如 SELECT column as averyveryveryverylongalias more than 30 characters FROM Table name 它返回错误ORA 00972 标识符太长 有什么技巧
  • Oracle 中的 MD5 (DBMS_OBFUSCATION_TOOLKIT.MD5)

    我正在尝试编写一个函数 从我到处收集的位中获取 MD5 哈希值 我想获得哈希的小写十六进制表示 到目前为止我有这个 CREATE OR REPLACE FUNCTION MD5 CADENA IN VARCHAR2 RETURN DBMS
  • ORA-01722-无效号码

    我在包中有一个查询抛出错误 ORA 01722 无效号码 我尝试了很多方法但没有运气 select h column from table1 h table2 c where c created date 17 MAY 17 and nvl
  • 无法从 Windows 服务连接到 Oracle(错误:ORA-12154: TNS: 无法解析服务名称 (12154))

    最新更新 2011 年 11 月 2 日上午 9 点 我尝试从服务运行 tnsping 它有效 但是 当我尝试连接时 仍然收到错误 12154 我现在完全困惑了 我不明白 tnsping 如何正常工作 但连接无法解析服务名称 由于某种原因
  • 具有动态参数的 Oracle Lag 函数

    我有一个具体的问题 我有一个包含无效值的表 我需要替换无效值 此处0 与之前的值大于0 困难是 使用更新或插入对我来说是不合适的 游标和更新就可以了 我唯一的方法是使用 Select 语句 当我使用lag col1 1 当情况发生时 我只得
  • 从 SQL 表在 SQL 中创建数据透视视图

    我有下表TEMP 我想使用 SQL 创建一个数据透视视图 排序依据CATEGORYASC 通过LEVEL降序和SETASC 并填写value 预期输出 我已尝试以下代码 但无法解决引发错误的聚合部分 SELECT FROM SELECT S
  • oracle 计算两个字符串中连续匹配的单词

    我想要一个返回两个字符串中单词的顺序匹配数的查询 例子 Table Id column1 column2 result 1 foo bar live foo bar 2 2 foo live tele foo tele 1 3 bar fo
  • SQL 连接中的多个条件

    如何指定多个条件SQL加入 我知道A key B key除此之外是强制性的 以下对于指定多个条件是否正确SQL ON A key B key and or cond1 and or cond2 etc OR ON A key B key w
  • 什么时候空值在列中“安全”?

    设计数据库时是否存在允许列为空与 3nf 规范化的一般经验法则 我有一个表 其中的列主要由空值 85 组成 但表大小不超过 10K 记录 不是很大 它主要用于日志记录和记录保存 因此大多数事务将是插入和选择 而不是更新 我试图同时考虑性能和
  • 如何将大型 XML 字符串插入 Oracle 表中?

    我想将一个大的 XML 字符串插入到我的表中 我的表是 test id xml column XMLType 当我插入值时 它返回 字符串文字太长 错误 我上网查了一下 大家都说把数据类型改成CLOB 但我想存储相同的数据类型 XMLTyp
  • 如何关闭 Oracle 密码过期功能?

    我正在使用 Oracle 进行开发 我经常用于重建数据库的引导帐户的密码已过期 如何永久关闭该用户 以及所有其他用户 的密码过期功能 我使用的是 Oracle 11g 默认情况下密码会过期 要更改 Oracle 中某个用户配置文件的密码过期
  • Oracle 中的 TO_Char 数字格式模型

    我不完全理解如何使用 to char 函数将数字转换为具有适当格式模型的字符串 实际数字具有以下格式 使用逗号作为小数点分隔符 始终为 5 个小数 整数最多可达 6 可能是无限的 但目前绝不会超过 6 数字可以是正数或负数 数字可以以 0
  • sql 查询查找匹配属性

    我目前正在做一个类似易货系统的系统 情况是这样的 客户 Jasmine 要求输入 NAME 属性 她需要寻找的内容 并输入 SEEK 属性 她需要寻找的内容 为了获得结果 SEEK 属性必须与其他客户的 Name 属性匹配 其他客户的 SE
  • 使用 Oracle 中的 Join 查询进行更新

    查询有什么问题 它无限期地执行 UPDATE table1 t1 SET t1 col t1 Output SELECT t2 col t3 Output t2 col FROM tabl2 t3 LEFT JOIN table1 t2 O
  • 为什么我的层次结构查询显示重复记录?

    我的要求是找到一个月中所有过去的天数 以下是我的示例查询 CREATE TABLE custom date full sno NUMBER curr date DATE INSERT INTO custom date full VALUES
  • oracle sql中where条件的动态数量

    我需要为报告工具中的提示编写一条sql 我得到变量中用 分隔的多个值的列表 并且这些值的数量可以变化 例如1 abc def eg2 abc def xyz 现在我需要在oracle中编写这种形式的sql 逻辑上 select someth
  • 在oracle sql中创建日期差异的自定义函数,排除周末和节假日

    我需要计算两个日期之间的天数decimal 不包括周末和节假日 by 使用自定义函数在 Oracle SQL 中 网站上也有类似的问题 然而 正如我所看到的 它们都没有要求使用自定义函数将输出作为十进制 我需要小数的原因是为了之后能够使用
  • 无效号码错误!似乎无法绕过它

    Oracle 10g 数据库 我有一张桌子叫s contact 这个表有一个字段叫做person uid This person uid字段是 varchar2 但包含某些行的有效数字和其他行的无效数字 例如 一行可能有一个person u
  • 如何选择集合的第一个元素作为查询的列

    表 t 有两列 a 和 b a是整数 b是集合 我想为每一行选择 a 和集合 b 的第一个值 我试过了 但没用 WITH s a b AS SELECT 1 ff FROM DUAL UNION ALL SELECT 1 ee FROM D
  • 为什么Mysql的Group By和Oracle的Group by行为不同

    为什么Mysql的Group By和Oracle的Group by行为不同 我多次发现 Mysql group By 功能和 Oracle 的 GroupBy 功能表现不同 很多时候我在Oracle中发现错误 这实际上是错误的查询 但是My

随机推荐

  • 有趣的Hack-A-Sat黑掉卫星挑战赛——被破坏的阿波罗计算机(解法二)

    国家太空安全是国家安全在空间领域的表现 随着太空技术在政治 经济 军事 文化等各个领域的应用不断增加 太空已经成为国家赖以生存与发展的命脉之一 凝聚着巨大的国家利益 太空安全的重要性日益凸显 1 而在信息化时代 太空安全与信息安全紧密地结合
  • 时间与时间戳的转换

    什么是时间戳 时间戳是指格林威治时间自1970年1月1日 00 00 00 GTM 至当前时间的总秒数 它也被称为Unix时间戳 Unix Timestamp 时间戳是能够表示一份数据在一个特定时间点已经存在的完整的可验证的数据 通常是一个
  • org.json.JSONException: No value for thumbnail_pic

    在JSON解析中会遇到一种错误 很常见的错误 是因为我们解析方法getString 是因为JSON里面不存在这个value 我们可以用另外一个方法optString 这个方法如果不存在value会返回 空字符串 不会报异常 为了安全起见我一
  • ECS突发性能型t6和轻量应用服务器区别和选择?

    这两款对比的话 个人还是首选推荐使用ECS突发性能型t6 毕竟是ecs服务器 功能上全面一些的 关于什么是阿里云ECS共享型S6 计算型 突发性能型 操作和地域镜像等系统选择 前往阿里云官网产品文档了解轻量服务器和突发型相关内容 共享型 云
  • 以带头结点的循环链表表示队列,并且只设一个指针指向队尾元素。

    出队的时候一定要注意是不是最后一个元素出队 假设以带头结点的循环链表表示队列 并且只设一个指针指向队尾元素结点 试编写相 应的初始化 入队以及出队算法 include
  • Minio安装

    Minio安装 想要了解 Minio 直接去官网查看 官网 https min io GitHub地址 https github com minio minio minio概述文档 http docs minio org cn minio
  • 文件中的类都不能进行设计,因此未能为该文件显示设计器。设计器检查出文件中有以下类: FormMain --- 未能加载基类“WinForm.Win.FormsBase.FormMainBase”。请确...

    出现该问题的原因 FormMain从FormMainBase继承之后 一旦修改FormMainBase就会出现这个问题 解决方案 1 4是搜索网友的 1 关闭VS所有窗口 后重启 即可返回正常 2 第一种方案不成功 关闭VS所有窗口 点击解
  • 重置计算机后无法开机,win10重置此电脑失败怎么办_win10重置此电脑失败无法开机修复方法...

    当win10系统使用时间长了 难免会有一些电脑故障的出现 这时有些用户就会选择使用重置电脑的方式来解决 但是最近有用户再给自己的win10系统进行重置时总是出现失败的情况 那么win10重置此电脑失败怎么办呢 下面就来告诉大家win10重置
  • TCP/UDP

    TCP与UDP的相关知识 TCP的三次握手具体发生在哪个函数中 补充知识 TCP的三次握手具体发生在哪个函数中 在Linux编程中 在进行TCP网络编程时 通常分为客户端和服务器端两个模块 客户端 fd socket 利用socket生成文
  • jvm原理--简单的程序加载分析

    package com bzu csh 类加载器加载 class文件 gt 初始化static声明 并不赋值 gt 调用类 gt static声明赋值由上到下 程序开始运行 首先执行main方法 执行main方法第一条语句 调用Single
  • 时序预测

    时序预测 MATLAB实现SARIMA时间序列预测 arima函数 经常还会遇到一种情况 即某些时间序列中存在明显的周期性变化 这种周期是由于季节性变化 季度 月度等 引起的 ARIMA的扩展支持SARIMA 它支持对该系列的季节性成分进行
  • 以太坊区块链学习之在私链上部署合约

    上一篇博客介绍了如何搭建私链并在私链上创建账户 挖矿 查看余额 本篇将介绍在私链上部署合约并与之交互 本篇开发环境为MacOS 10 12 建议读者使用macOS系统或者Ubuntu系统 第一步 进入geth客户端 启动私链 进入geth客
  • 网络基础-应用层协议-HTTP/HTTPS

    HTTP HTTPS HTTP 基本概念 协议格式 请求报文 请求方法 请求资源地址 协议版本 应答报文 常见Header 常见状态码与状态描述 Cookie Session http协议特点 HTTPS 基本概念 对称加密与非对称加密 数
  • magic-api简单例子

    1 magic api magic api 是一个基于Java的接口快速开发框架 通过magic api提供的UI界面完成编写接口 无需定义Controller Service Dao Mapper XML VO等Java对象即可完成常见的
  • 继承、重写、重载、 abstract 抽象、 final、向上(向下)转型、多态

    继承 父类 公共的属性和方法 子类 特有的属性和方法 重写 override 1 继承 2 子类拥有和父类的一样的方法 方法名相同 参数列表相同 返回值类型可以缩小 访问控制权限可以方法 可声明的异常 重载 overload 1个类中 方法
  • [LeetCode-01]-Two Sum(求和)

    文章目录 题目相关 Solution 1 暴力求解 2 暴力求解方法改进 3 不可行的方案 后记 每周完成一个ARTS Algorithm Review Tip Share ARTS Algorithm 每周至少做一个 leetcode 的
  • OpenLooKeng连接hive出现UnknownHostException

    背景 最近在验证一个计算的方案 使用的是华为开源的数据虚拟化引擎OpenLookeng hetu 底层基于presto引擎来提供交互式查询分析能力 先前成功部署过一套 添加hive mysql等数据源一切正常 后重新在其他环境进行部署时 连
  • 老胡的周刊(第106期)

    老胡的信息周刊 1 记录这周我看到的有价值的信息 主要针对计算机领域 内容主题极大程度被我个人喜好主导 这个项目核心目的在于记录让自己有印象的信息做一个留存以及共享 项目 quivr 2 Quivr 是您在云中的第二个大脑 让您轻松存储和检
  • windows10-cuda10.1-cudnn7-tensorflow1.4.0配置流程

    1 下载cuda版本 cuda 10 1 105 win10 network exe 并按照流程安装 安装完后 打开cmd 输入 nvcc V 回车查看是否有版本信息 若出现版本信息 则证明nvcc安装成功 2 下载cudnn版本 cudn
  • oracle批量绑定 forall bulk collect用法以及测试案例

    一 如何使用批挷定提高性能 How Do Bulk Binds Improve Performance 在PL SQL 和SQL引擎 engines 中 太多的上下文切换 context switches 会影响性能 这个会发生在当一个循环