BulkCollect FORALL
How to use forall here
SQL> DECLARE
2 CURSOR driver IS SELECT * FROM SCOTT.EMP;
3 TYPE recs_type IS TABLE OF SCOTT.EMP%ROWTYPE INDEX BY BINARY_INTEGER;
4 recs recs_type;
5 BEGIN
6 OPEN driver;
7 FETCH driver BULK COLLECT INTO recs;
8 CLOSE driver;
9 FORALL i IN 1..recs.COUNT
10 UPDATE MY_EMP SET ENAME = UPPER(recs(i).ENAME)
11 WHERE EMPNO = recs(I).EMPNO;
12 END;
13 /
UPDATE MY_EMP SET ENAME = UPPER(recs(i).ENAME)
ERROR at line 10:
ORA-06550: line 10, column 35:
PLS-00436: implementation restriction: cannot reference fields of BULK In-BIND
table of records
ORA-06550: line 10, column 35:
PLS-00382: expression is of wrong type
ORA-06550: line 11, column 17:
PLS-00436: implementation restriction: cannot reference fields of BULK In-BIND
table of records
ORA-06550: line 11, column 17:
PLS-00382: expression is of wrong type
ORA-06550: line 10, column 35:
PL/SQL: ORA-22806: not an object or REF
ORA-06550: line 10, column 3:
PL/SQL: SQL Statement ignored
The statement boils down to
update emp set ename=upper(ename);
And that is the most efficient implementation there is, BULK colllect will be several magnitudes slower.
No solution is required, or this must be an exercise in the course 'How to build an unscalable application'
Sybrand Bakker
Senior Oracle DBA
Similar Messages
-
BulkCollect ...ForAll ... Limit question
Hi,
I'm using the BulkCollect and ForAll method. What is the appropriate quantity to use in the "LIMIT" parameter in the BulkCollect statement. My cursor has 350 000 records. What would be the best number to put there?
FETCH CurStatsVtesReg BULK COLLECT
INTO vNoCentreDistribution
,vCliIdClient
,vNoClient
,vPrdIdProduit
,vNoProduit
,vCodeRegionTerritoriale
,vQteVteReg
LIMIT 50000;
ThanksExactly. Only you are familiar with the environment your software is going to run in.
On a data warehouse, I'm using a LIMIT of 10000 for a bulk process that sync's with production - as that is the optimal setting ito performance and PGA utilisation.
On another high volume platform, I'm using a LIMIT of 100.
Horse for courses. And do not let anyone else tell you otherwise. There are no fixed magic and golden parameters in performance tuning.
Best that can be given to you is performance guidelines. However, at the end of the day no two production environments are identical in every respect. So what works in one may not work in another - and often not.
A word of advice though. Do not hardcode the LIMIT clause in your code. Consider using package constants instead.
Reason. This allows the DBA or Production Admin/Owner to further fine-tune your code. During initial deployment your process may have been run with others during peak periods and the LIMIT clause reduced to a 100 or even less.
Now the same code is scheduled for off-peak periods where it will have most of the free system memory of that platform for itself. Having a bunch of constants to change allows the LIMIT to be changed.
In other words, when you code for performance, provide performance knobs for the guys in production to turn up and down. Instead of hardcoding performance related parameters in your code. -
Using forall and bulkcollect together
Hey group,
I am trying to use bulk collect and forall together.
i have bulk collect on 3 columns and insert is on more than 3 columns.can anybody tell me how to reference those collection objects in bulk collect statement.
you can see the procedure,i highlighted things am trying.
Please let me know,if am clear enough.
PROCEDURE do_insert
IS
PROCEDURE process_insert_record
IS
CURSOR c_es_div_split
IS
SELECT div_id
FROM zrep_mpg_div
WHERE div_id IN ('PC', 'BP', 'BI', 'CI', 'CR');
PROCEDURE write_record
IS
CURSOR c_plan_fields
IS
SELECT x.comp_plan_id, x.comp_plan_cd, cp.comp_plan_nm
FROM cp_div_xref@dm x, comp_plan@dm cp
WHERE x.comp_plan_id = cp.comp_plan_id
AND x.div = v_div
AND x.sorg_cd = v_sorg_cd
AND x.comp_plan_yr = TO_NUMBER (TO_CHAR (v_to_dt, 'yyyy'));
TYPE test1 IS TABLE OF c_plan_fields%ROWTYPE
INDEX BY BINARY_INTEGER;
test2 test1;
BEGIN -- write_record
OPEN c_plan_fields;
FETCH c_plan_fields bulk collect INTO test2;
CLOSE c_plan_fields;
ForAll X In 1..test2.last
INSERT INTO cust_hier
(sorg_cd, cust_cd, bunt, --DP
div,
from_dt,
to_dt,
cust_ter_cd, cust_rgn_cd, cust_grp_cd,
cust_area_cd, sorg_desc, cust_nm, cust_ter_desc,
cust_rgn_desc, cust_grp_desc, cust_area_desc,
cust_mkt_cd, cust_mkt_desc, curr_flag,
last_mth_flag, comp_plan_id, comp_plan_cd,
comp_plan_nm, asgn_typ, lddt
VALUES (v_sorg_cd, v_cust_cd, v_bunt, --DP
v_div,
TRUNC (v_from_dt),
TO_DATE (TO_CHAR (v_to_dt, 'mmddyyyy') || '235959',
'mmddyyyyhh24miss'
v_ter, v_rgn, v_grp,
v_area, v_sorg_desc, v_cust_nm, v_cust_ter_desc,
v_rgn_desc, v_grp_desc, v_area_desc,
v_mkt, v_mkt_desc, v_curr_flag,
v_last_mth_flag, test2(x).comp_plan_id,test2(x).comp_plan_cd,
test2(x).comp_plan_nm, v_asgn_typ, v_begin_dt
v_plan_id := 0;
v_plan_cd := 0;
v_plan_nm := NULL;
v_out_cnt := v_out_cnt + 1;
IF doing_both
THEN
COMMIT;
ELSE
-- commiting v_commit_rows rows at a time.
IF v_out_cnt >= v_commit_cnt
THEN
COMMIT;
p.l ( 'Commit point reached: '
|| v_out_cnt
|| 'at: '
|| TO_CHAR (SYSDATE, 'mm/dd hh24:mi:ss')
v_commit_cnt := v_commit_cnt + v_commit_rows;
END IF;
END IF;
END write_record;Ugly code.
Bulk processing does what in PL? One and one thing only. It reduces context switching between the PL and SQL engines. That is it. Nothing more. It is not magic that increases performance. And there is a penalty to pay for the reduction in context switching - memory. Very expensive PGA memory.
To reduce the context switches, bigger chunks of data are passed between the PL and SQL engines. You have coded a single fetch for all the rows from the cursor. All that data collected from the SQL engine has to be stored in the PL engine. This requires memory. The more rows, the more memory. And the memory used is dedicated non-shared server memory. The worse kind to use on a server where resources need to be shared in order for the server to scale.
Use the LIMIT clause. That controls how many rows are fetched. And thus you manage just how much memory is consumed.
And why the incremental commit? What do you think you are achieving with that? Except consuming more resources by doing more commits.. not to mention risking data integrity as this process can now fail and result in only partial changes. And only changing some of the rows when you need to change all the rows is corrupting the data in the database in my book.
Also, why use PL at all? Surely you can do a INSERT into <table1> SELECT .. FROM <table2>,<table3> WHERE ...
And with this, you can also use parallel processing (DML). You can use direct path inserts. You do not need a single byte of PL engine code or memory. You do not have to ship data between the PL and SQL engines. What you now have is fast performing code that can scale. -
How to use Bulk Collect and Forall
Hi all,
We are on Oracle 10g. I have a requirement to read from table A and then for each record in table A, find matching rows in table B and then write the identified information in table B to the target table (table C). In the past, I had used two ‘cursor for loops’ to achieve that. To make the new procedure, more efficient, I would like to learn to use ‘bulk collect’ and ‘forall’.
Here is what I have so far:
DECLARE
TYPE employee_array IS TABLE OF EMPLOYEES%ROWTYPE;
employee_data employee_array;
TYPE job_history_array IS TABLE OF JOB_HISTORY%ROWTYPE;
Job_history_data job_history_array;
BatchSize CONSTANT POSITIVE := 5;
-- Read from File A
CURSOR c_get_employees IS
SELECT Employee_id,
first_name,
last_name,
hire_date,
job_id
FROM EMPLOYEES;
-- Read from File B based on employee ID in File A
CURSOR c_get_job_history (p_employee_id number) IS
select start_date,
end_date,
job_id,
department_id
FROM JOB_HISTORY
WHERE employee_id = p_employee_id;
BEGIN
OPEN c_get_employees;
LOOP
FETCH c_get_employees BULK COLLECT INTO employee_data.employee_id.LAST,
employee_data.first_name.LAST,
employee_data.last_name.LAST,
employee_data.hire_date.LAST,
employee_data.job_id.LAST
LIMIT BatchSize;
FORALL i in 1.. employee_data.COUNT
Open c_get_job_history (employee_data(i).employee_id);
FETCH c_get_job_history BULKCOLLECT INTO job_history_array LIMIT BatchSize;
FORALL k in 1.. Job_history_data.COUNT LOOP
-- insert into FILE C
INSERT INTO MY_TEST(employee_id, first_name, last_name, hire_date, job_id)
values (job_history_array(k).employee_id, job_history_array(k).first_name,
job_history_array(k).last_name, job_history_array(k).hire_date,
job_history_array(k).job_id);
EXIT WHEN job_ history_data.count < BatchSize
END LOOP;
CLOSE c_get_job_history;
EXIT WHEN employee_data.COUNT < BatchSize;
END LOOP;
COMMIT;
CLOSE c_get_employees;
END;
When I run this script, I get
[Error] Execution (47: 17): ORA-06550: line 47, column 17:
PLS-00103: Encountered the symbol "OPEN" when expecting one of the following:
. ( * @ % & - + / at mod remainder rem select update with
<an exponent (**)> delete insert || execute multiset save
merge
ORA-06550: line 48, column 17:
PLS-00103: Encountered the symbol "FETCH" when expecting one of the following:
begin function package pragma procedure subtype type use
<an identifier> <a double-quoted delimited-identifier> form
current cursorWhat is the best way to code this? Once, I learn how to do this, I apply the knowledge to the real application in which file A would have around 200 rows and file B would have hundreds of thousands of rows.
Thank you for your guidance,
SeyedHello BlueShadow,
Following your advice, I modified a stored procedure that initially was using two cursor for loops to read from tables A and B to write to table C to use instead something like your suggestion listed below:
INSERT INTO tableC
SELECT …
FROM tableA JOIN tableB on (join condition).I tried this change on a procedure writing to tableC with keys disabled. I will try this against the real table that has primary key and indexes and report the result later.
Thank you very much,
Seyed -
Hi,
I have written this procedure and I am getting
LINE/COL ERROR
69/5 PL/SQL: SQL Statement ignored
83/11 PL/SQL: ORA-00947: not enough values
CREATE OR REPLACE PROCEDURE f_t_s (P_F IN VARCHAR2)
IS
TYPE quote_cols_rt IS RECORD
twitter_gid twitter.twitter_gid%TYPE,
twitter_xid twitter.twitter_xid%TYPE,
origin_location_type twitter.origin_location_type%TYPE,
destination_location_type twitter.destination_location_type%TYPE,
is_hazardous twitter.is_hazardous%TYPE,
perspective twitter.perspective%TYPE,
twitter_option twitter.twitter_option%TYPE,
servprov_gid twitter.servprov_gid%TYPE,
origin_search_value twitter.origin_search_value%TYPE,
destination_search_value twitter.destination_search_value%TYPE,
domain_name twitter.domain_name%TYPE,
is_customer_rates_only twitter.is_customer_rates_only%TYPE
TYPE twitter_cols_tt IS TABLE OF twitter_cols_rt INDEX BY PLS_INTEGER;
twitter_cols_t twitter_cols_tt;
BEGIN
SELECT all_spec twitter_gid,
SUBSTR(all_spec,7) twitter_xid,
'location_code' origin_location_type,
'location_code' destination_location_type,
'N' is_hazardous,
'B' perspective,
'O' twitter_option,
'INDIA.'||servprov_xid servprov_gid,
SUBSTR(tm,1,2) ||':'||SUBSTR(tm,3) origin_search_value,
Location_xid destination_search_value,
'INDIA' domain_name,
'N' is_customer_rates_only
BULK COLLECT INTO twitter_cols_t
FROM
(SELECT x.*,
Q.twitter_gid
FROM
(SELECT 'INDIA.FS_'||H.servprov_xid||'_'||D.Location_xid||'_'||T.tm all_spec,
servprov_xid ,
tm ,
Location_xid
FROM
(SELECT Location_xid
FROM location
WHERE location_xid IN ('1')
AND domain_name ='INDIA'
) D,
(SELECT servprov_xid
FROM servprov
WHERE(servprov_gid LIKE 'P_F%'
AND SUBSTR(servprov_xid,2,1) IN ('2'))
OR servprov_gid ='INDIA.FRESHMISC'
) H,
(SELECT lpad(rownum-1,2,'0') ||'00' tm FROM sea WHERE rownum<25
UNION
SELECT lpad(rownum-1,2,'0') ||'30' tm FROM sea WHERE rownum<25
) T
) x,
(SELECT * FROM twitter WHERE twitter_gid LIKE 'INDIA.FS%'
) Q
WHERE x.all_spec=Q.twitter_gid(+)
WHERE twitter_gid IS NULL;
FORALL i in 1 .. twitter_cols_t.count
--LOOP
INSERT
INTO twitter
(twitter_gid ,
twitter_xid ,
origin_location_type ,
destination_location_type,
is_hazardous ,
perspective ,
twitter_option ,
servprov_gid ,
origin_search_value ,
destination_search_value ,
domain_name ,
is_customer_rates_only)
VALUES
twitter_cols_t (i);
--END LOOP;
END f_t_s;
Any idea where I am doing mistake
Thanks in Advance,
DIDIHi,
That was my typing mistake....
This is correct code
CREATE OR REPLACE PROCEDURE f_t_s (P_F IN VARCHAR2)
IS
TYPE twitter_cols_rt IS RECORD
twitter_gid twitter.twitter_gid%TYPE,
twitter_xid twitter.twitter_xid%TYPE,
origin_location_type twitter.origin_location_type%TYPE,
destination_location_type twitter.destination_location_type%TYPE,
is_hazardous twitter.is_hazardous%TYPE,
perspective twitter.perspective%TYPE,
twitter_option twitter.twitter_option%TYPE,
servprov_gid twitter.servprov_gid%TYPE,
origin_search_value twitter.origin_search_value%TYPE,
destination_search_value twitter.destination_search_value%TYPE,
domain_name twitter.domain_name%TYPE,
is_customer_rates_only twitter.is_customer_rates_only%TYPE
TYPE twitter_cols_tt IS TABLE OF twitter_cols_rt INDEX BY PLS_INTEGER;
twitter_cols_t twitter_cols_tt;
BEGIN
SELECT all_spec twitter_gid,
SUBSTR(all_spec,7) twitter_xid,
'location_code' origin_location_type,
'location_code' destination_location_type,
'N' is_hazardous,
'B' perspective,
'O' twitter_option,
'INDIA.'||servprov_xid servprov_gid,
SUBSTR(tm,1,2) ||':'||SUBSTR(tm,3) origin_search_value,
Location_xid destination_search_value,
'INDIA' domain_name,
'N' is_customer_rates_only
BULK COLLECT INTO twitter_cols_t
FROM
(SELECT x.*,
Q.twitter_gid
FROM
(SELECT 'INDIA.FS_'||H.servprov_xid||'_'||D.Location_xid||'_'||T.tm all_spec,
servprov_xid ,
tm ,
Location_xid
FROM
(SELECT Location_xid
FROM location
WHERE location_xid IN ('1')
AND domain_name ='INDIA'
) D,
(SELECT servprov_xid
FROM servprov
WHERE(servprov_gid LIKE 'P_F%'
AND SUBSTR(servprov_xid,2,1) IN ('2'))
OR servprov_gid ='INDIA.FRESHMISC'
) H,
(SELECT lpad(rownum-1,2,'0') ||'00' tm FROM sea WHERE rownum<25
UNION
SELECT lpad(rownum-1,2,'0') ||'30' tm FROM sea WHERE rownum<25
) T
) x,
(SELECT * FROM twitter WHERE twitter_gid LIKE 'INDIA.FS%'
) Q
WHERE x.all_spec=Q.twitter_gid(+)
WHERE twitter_gid IS NULL;
FORALL i in 1 .. twitter_cols_t.count
--LOOP
INSERT
INTO twitter
(twitter_gid ,
twitter_xid ,
origin_location_type ,
destination_location_type,
is_hazardous ,
perspective ,
twitter_option ,
servprov_gid ,
origin_search_value ,
destination_search_value ,
domain_name ,
is_customer_rates_only)
VALUES
twitter_cols_t (i);
--END LOOP;
END f_t_s;
Thanks,
DIDI -
Deletion is taking long time using forall
Hi,
i am inserting and deleting the rows using forall. insert taking less time to inset the rows but while coming to
deletion it is taking more than 5 days long time to delete 18.5 million rows in a table using forall.
the main table having 70 million rows.
the code is..
FETCH ref_typ BULK COLLECT INTO l_id_tbl LIMIT 10000;
begin
FORALL i in 1..l_id_tbl.COUNT
INSERT INTO change_test (id,
history,
transaction,
date)
VALUES (seq.nextval,
'CHANGE_HIS',
l_id_tbl(i),
sysdate);
exception
when others then
null;
end;
begin
FoRALL i in 1..l_id_tbl.COUNT
DELETE FROM change_his
where id = l_id_tbl(i);
exception
when others then
null;
end;
end loop;
so please give me a good solution to delete the rows less than 5 days..Why are you wanting to do this using BULK COLLECT and FORALL?
Why not just "insert ... select ..." and "delete ..."?
Loading records into expensive PGA memory to insert them back on the database is bound to be slower (and use more server resources) than just doing a straight insert ... select ... statement.
Explain exactly what you are trying to do.
Re: 2. How do I ask a question on the forums? -
Hello All,
May i know why the following example from Oracle documentation is failing.
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 succeeds. */
FORALL i in 1..3
UPDATE coords SET (x, y) = (pairs(i).m, pairs(i).n)
WHERE x = nums(i);
END;
Error:
Error starting at line 37 in command:
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;
Error report:
ORA-06550: line 9, column 34:
PL/SQL: ORA-01767: UPDATE ... SET expression must be a subquery
ORA-06550: line 9, column 7:
PL/SQL: SQL Statement ignored
06550. 00000 - "line %s, column %s:\n%s"
*Cause: Usually a PL/SQL compilation error.
*Action:
Thank you.Based on your advise i did the below example,
DECLARE
TYPE PairTab IS TABLE OF Pair;
pairs PairTab := PairTab(Pair(1,2), Pair(3,4), Pair(5,6),Pair(7,8),Pair(9,10));
TYPE NumTab IS TABLE OF NUMBER;
nums NumTab := NumTab(1,2,3,4,5);
vnum1 NUMBER;
vnum2 number;
BEGIN
FORALL i IN 1 .. 5
UPDATE coords
SET (x, y) = (SELECT pairs(i).m, pairs(i).n
FROM dual)
WHERE x = nums(i);
END;
INSERT INTO coords VALUES(1,10);
INSERT INTO coords VALUES(2,10);
insert into coords values(3,10);
INSERT INTO coords VALUES(4,10);
INSERT INTO coords VALUES(5,10);
commit;
After insertng the data into coords table i execute the above plsql block i got the below output.I wonder how come rows 2,3 & 5 are updated with 9,10.
Output:
1
2
9
10
9
10
7
8
9
10 -
Hi All,
I am using FORALL for inserting 40000 records from the file to the table. After reading data from the file it has been stored in type Table array, But while executing FORALL statement its giving ora-00600. error.
It is a memory error, some where I read to using LIMIT in fetch. But I am not using any fetch or Cursor. Can any body Help.
SKM
=================== ================= Package Code
create or replace package insertpackage as
TYPE tabSNO IS TABLE OF VARCHAR2(30) INDEX BY BINARY_INTEGER;
TYPE tabSNO1 IS TABLE OF VARCHAR2(1) INDEX BY BINARY_INTEGER;
TYPE tabSDATE IS TABLE OF VARCHAR2(20) INDEX BY BINARY_INTEGER;
TYPE SNO IS TABLE OF NUMBER(5) INDEX BY BINARY_INTEGER;
procedure insertpro(snoArray IN SNO, sDate IN tabSDATE, sArray IN tabSNO, sArray1 IN tabSNO1);
end insertpackage;
create or replace package body insertpackage as
procedure insertpro(snoArray IN SNO, sDate IN tabSDATE, sArray IN tabSNO, sArray1 IN tabSNO1) is
begin
forall i in 1..sArray.last
insert into test(s_no, s_date, s_co, s_type)
values(snoArray(i), TO_DATE(sDate(i),'YYYY-MM-DD HH24.MI.SS'), sArray(i), sArray1(i));
end;
end insertpackage;
/Hi User,
The error
implementation restriction: cannot reference fields of BULK In-BIND table of recordsis because bulk bind cannot use table of composite types.
Please See the below,
http://dba-blog.blogspot.com/2005/08/using-of-bulk-collect-and-forall-for.html
And rewrite your code like this,
DECLARE
CURSOR EMP_CUR
IS
SELECT EMPNO, ENAME
FROM EMP;
TYPE TAB_EMP_EMPNO IS TABLE OF EMP.EMPNO%TYPE;
V_TAB_EMPNO TAB_EMP_EMPNO;
TYPE TAB_EMP_ENAME IS TABLE OF EMP.ENAME%TYPE;
V_TAB_ENAME TAB_EMP_ENAME;
BEGIN
OPEN EMP_CUR;
FETCH EMP_CUR BULK COLLECT INTO V_TAB_EMPNO, V_TAB_ENAME;
FORALL I IN V_TAB_EMPNO.FIRST .. V_TAB_EMPNO.LAST
INSERT INTO EMP_TEMP
(EMPNO, ENAME
VALUES (V_TAB_EMPNO (I), V_TAB_ENAME (I)
CLOSE EMP_CUR;
END;Thanks,
Shankar -
Using Sequence in FORALL Statement
I'm using a package to get the nextval in a sequence object. Basically, it is a function that returns select user_seq.nextval from dual.
But I get 'column not allowed here' error. PL/SQL: ORA-00984: column not allowed here
OPEN users_ins ;
LOOP
FETCH naf_users_ins
BULK COLLECT
INTO arr_person_key
, arr_person_id
, arr_first_name
, arr_middle_name
, arr_last_name
, arr_username
, arr_user_status_seq
, arr_creation_date
, arr_comments
LIMIT 100 ;
FORALL idx IN 1 .. arr_person_key.COUNT
SAVE EXCEPTIONS
INSERT INTO users
( user_seq
, person_key
, person_id
, first_name
, middle_name
, last_name
, username
, user_status_seq
, creation_date
, comments
VALUES ( *pkg_admin.get_nextval*
, arr_person_key(idx)
, arr_person_id(idx)
, arr_first_name(idx)
, arr_middle_name(idx)
, arr_last_name(idx)
, arr_username(idx)
, arr_user_status_seq(idx)
, arr_creation_date(idx)
, arr_comments(idx)
EXIT WHEN users_ins%NOTFOUND ;
END LOOP ;
CLOSE users_ins;
c/code]Hi,
I've recently completed a similar task, but I declared a collection with the rowtype of the table. I also added the sequence in the query of my cursor. Take a look at the following example.
CREATE OR REPLACE PROCEDURE Insert_OR_ExternalService
IS
TYPE externalService_tbl IS TABLE OF OR_ExternalService%ROWTYPE;
externalService externalService_tbl;
CURSOR OR_ExternalServiceCursor
IS
select
SEQ_ID.NEXTVAL as "Id",
column as "ExternalId",
column as "ExternalSystem",
from table1
where ...;
BEGIN
OPEN OR_ExternalServiceCursor;
LOOP
FETCH OR_ExternalServiceCursor BULK COLLECT INTO externalService LIMIT 1000;
FORALL i IN 1 .. externalService.count
INSERT INTO OR_ExternalService values externalService(i);
COMMIT;
EXIT WHEN OR_ExternalServiceCursor%NOTFOUND;
END LOOP;
CLOSE OR_ExternalServiceCursor;
END; -
PLS-00435: DML statement without BULK In-BIND cannot be used inside FORALL
See code below: Have googled the error but not sure how to fix the code. ANy help would be most greatful.
DECLARE
CURSOR c1
IS
SELECT /*+ parallel(tr,8)*/ DISTINCT tr.ACC ACC, tr.MET_ID METID, to_char(max(TRUNC(PKG_LSR_IMPOSITION.convertXXMMDDToDate(tr.DATETR0))),'YYYY') dt
FROM CTTR0FIL_CDC tr
JOIN transaction_action_sd sd on (sd.TRANSACTION_CODE = tr.RESULT)
WHERE sd.ACTION_TYPE = 'PAYMENT'
and to_char(TRUNC(PKG_LSR_IMPOSITION.convertXXMMDDToDate(tr.DATETR0)),'YYYY') = '1987'
GROUP BY tr.ACC, tr.MET_ID, to_char(TRUNC(PKG_LSR_IMPOSITION.convertXXMMDDToDate(tr.DATETR0)),'YYYY');
SUBTYPE PAY_DATES IS c1%ROWTYPE;
TYPE TMP_TABLE IS TABLE OF PAY_DATES INDEX BY PLS_INTEGER;
TBROWS TMP_TABLE;
temp VARCHAR2(100);
Begin
DBMS_OUTPUT.ENABLE(10000);
OPEN c1;
LOOP
FETCH c1 BULK COLLECT INTO TBROWS LIMIT 1000;
FORALL i IN 1..TBROWS.COUNT
SELECT /*+ parallel(da,8)*/ da.ACC INTO temp
FROM CTDA0FIL_CDC da
WHERE da.ACC = TBROWS(i).ACC AND da.MET_ID = TBROWS(i).MET_ID AND da.ACC = '07000006P' AND da.MET_ID = 4;
DBMS_OUTPUT.PUT_LINE('inside');
EXIT WHEN c1%notfound;
END LOOP;
CLOSE c1;
END;
Thanks
SimonI tried using a normal for loop instead of a forall loop (see code below). But this came up the error also see below. Or can I not use selects in this way???
OPEN c1;
LOOP
FETCH c1 BULK COLLECT INTO tbrows LIMIT 1000;
FOR i IN TBROWS.FIRST .. TBROWS.LAST
LOOP
SELECT /*+ parallel(da,8)*/ da.ACC INTO temp
FROM CTDA0FIL_CDC da WHERE da.ACC = TBROWS(i).ACC AND da.MET_ID = TBROWS(i).MET_ID AND da.ACC = '07000006P' AND da.MET_ID = 4;
DBMS_OUTPUT.PUT_LINE('inside');
EXIT WHEN c1%notfound;
END LOOP;
END LOOP;
CLOSE c1;
END;
Error:
DECLARE
ERROR at line 1:
ORA-06502: PL/SQL: numeric or value error
ORA-06512: at line 24 -
FORALL MERGE statement works in local database but not over database link
Given "list", a collection of NUMBER's, the following FORALL ... MERGE statement should copy the appropriate data if the record specified by the list exists on both databases.
forall i in 1..list.count
merge into tbl@remote t
using (select * from tbl
where id = list(i)) s
on (s.id = t.id)
when matched then
update set
t.status = s.status
when not matched then
insert (id, status)
values (s.id, s.status);
But this does not work. No exceptions, but target table's record is unchanged and "sql%rowcount" is 0.
If the target table is in the local database, the exact same statement works:
forall i in 1..list.count
merge into tbl2 t
using (select * from tbl
where id = list(i)) s
on (s.id = t.id)
when matched then
update set
t.status = s.status
when not matched then
insert (id, status)
values (s.id, s.status);
Does anyone have a clue why this may be a problem?
Both databases are on Oracle 10g.
Edited by: user652538 on 2009. 6. 12 오전 11:29
Edited by: user652538 on 2009. 6. 12 오전 11:31
Edited by: user652538 on 2009. 6. 12 오전 11:45Should throw an error in my opinion. The underlying reason for not working is basically because of
SQL> merge into t@remote t1
using ( select sys.odcinumberlist (1) from dual) t2
on (1 = 1)
when matched
then
update set i = 1
Error at line 4
ORA-22804: remote operations not permitted on object tables or user-defined type columnsSame reason as e.g.
insert into t@remote select * from table(sys.odcinumberlist(1,2,3))doesn't work. -
Issue in using Cursor+Dynamic SQL+ Bulk collect +FORALL
Hi,
I have a dynamic query which I need to use as a cursor to fetch records that inturn need to be inserted into a staging table.
The issue I am facing is I am not sure how to declare the variable to fetch the records into. Since I am using a dynamic cursor how do I declare it?
My code looks something like this -
TYPE c_details_tbl_type IS REF CURSOR;
c_details c_details_tbl_type;
TYPE c_det_tbl_type IS TABLE OF c_details%ROWTYPE INDEX BY PLS_INTEGER;
c_det_tbl c_det_tbl_type; -- ???
BEGIN
v_string1 := 'SELECT....'
v_string2 := ' UNION ALL SELECT....'
v_string3 := 'AND ....'
v_string := v_string1||v_string2||v_string3;
OPEN c_details FOR v_string;
LOOP
FETCH c_details BULK COLLECT
INTO c_det_tbl LIMIT 1000;
IF (c_det_tbl.COUNT > 0) THEN
DELETE FROM STG;
FORALL i IN 1..c_det_tbl.COUNT
INSERT INTO STG
VALUES (c_det_tbl(i));
END IF;
EXIT WHEN c_details%NOTFOUND;
END LOOP;
CLOSE c_details;
END
ThanksWhy the bulk collect? All that this does is slow down the read process (SELECT) and write process (INSERT).
Data selected needs (as a collection) to be pushed into the PGA memory of the PL/SQL engine. And then that very same data needs to be pushed again by the PL/SQL engine back to the database to be inserted. Why?
It is a lot faster, needs a lot less resources, with fewer moving parts, to simply instruct the SQL engine to do both these steps using a single INSERT..SELECT statement. And this can support parallel DML too for scalability when data volumes get large.
It is also pretty easy to make a single SQL statement like this dynamic and even support bind variables.
Simplicity is the ultimate form of elegance. Pushing data needlessly around is not simple and thus not a very elegant way to address the problem. -
Error in self increment varible value using "FORALL"
CREATE OR REPLACE PROCEDURE BULK_COLLCT_PASS
AS
TYPE VAR_TYP IS VARRAY (32767) OF VARCHAR2 (32767);
V_DSH_CM_NUMBER VAR_TYP;
V_DSH_DATE VAR_TYP;
V_DSH_TIME VAR_TYP;
V_DSD_CM_NUMBER VAR_TYP;
V_PLU_CODE VAR_TYP;
V_DSD_DATE VAR_TYP;
V_str_id VAR_TYP;
LN_ITM NUMBER:=0;
str_id number := 30001;
CURSOR CUR_DBMG_SAL_HEAD
IS
SELECT DSH.CM_NUMBER,D_DSH_CM_DATE, D_DSH_CM_TIME
FROM DBMG_SAL_HEAD DSH
WHERE ROWNUM<6;
BEGIN
OPEN CUR_DBMG_SAL_HEAD;
LOOP
FETCH CUR_DBMG_SAL_HEAD BULK COLLECT
INTO V_DSH_CM_NUMBER,
V_DSH_DATE,
V_DSH_TIME;
FOR indx IN V_DSH_CM_NUMBER.FIRST .. V_DSH_CM_NUMBER.LAST
LOOP
SELECT CM_NUMBER, PLU_CODE,V_DSH_DATE(indx)
BULK COLLECT
INTO V_DSD_CM_NUMBER, V_PLU_CODE,V_DSD_DATE
FROM DBMG_SAL_DETL DSD
WHERE DSD.CM_NUMBER = V_DSH_CM_NUMBER(indx);
--block1
Ln_Itm := 0;
FOR ind IN 1..V_DSD_CM_NUMBER.COUNT
loop
INSERT INTO PC_ALL_TAB
VALUES(V_DSH_CM_NUMBER(indx),
V_DSD_DATE(ind),
V_DSD_CM_NUMBER(ind),
V_PLU_CODE(ind),
LN_ITM,
str_id
LN_ITM := LN_ITM +1;
end loop;
--block2
END LOOP;
EXIT WHEN CUR_DBMG_SAL_HEAD%NOTFOUND;
END LOOP;
commit;
CLOSE CUR_DBMG_SAL_HEAD;
DBMS_OUTPUT.PUT_LINE('COMPLETE..!');
END ;
Hi,
I am using above code in which when code between "--block1 & --block2" is incrementing ln_itm value by 1 each time.
so that after completion of code o/p is as below.
SELECT DSH_CM_NUMBER, LN_ITM FROM PC_ALL_TAB;
DSH_CM_NUMBER LN_ITM
1 4177424 0
2 4177422 0
3 4177426 0
4 4177426 1
5 4177426 2
6 4177425 0
7 4177427 0
8 4177427 1
9 4177427 2
for each repeating value of cm_number its incresing by 1.
but i wan to change "--block1 to --block2" in "FORALL". i did this but i m nt getting o/p is incresing value of ln_itm.
kindly help me...
code after changed to "FORALL"
CREATE OR REPLACE PROCEDURE BULK_COLLCT_PASS
AS
TYPE VAR_TYP IS VARRAY (32767) OF VARCHAR2 (32767);
V_DSH_CM_NUMBER VAR_TYP;
V_DSH_DATE VAR_TYP;
V_DSH_TIME VAR_TYP;
V_DSD_CM_NUMBER VAR_TYP;
V_PLU_CODE VAR_TYP;
V_DSD_DATE VAR_TYP;
V_str_id VAR_TYP;
LN_ITM NUMBER:=0;
str_id number := 30001;
CURSOR CUR_DBMG_SAL_HEAD
IS
SELECT DSH.CM_NUMBER,D_DSH_CM_DATE, D_DSH_CM_TIME
FROM DBMG_SAL_HEAD DSH
WHERE ROWNUM<6;
BEGIN
OPEN CUR_DBMG_SAL_HEAD;
LOOP
FETCH CUR_DBMG_SAL_HEAD BULK COLLECT
INTO V_DSH_CM_NUMBER,
V_DSH_DATE,
V_DSH_TIME;
FOR indx IN V_DSH_CM_NUMBER.FIRST .. V_DSH_CM_NUMBER.LAST
LOOP
SELECT CM_NUMBER, PLU_CODE,V_DSH_DATE(indx)
BULK COLLECT
INTO V_DSD_CM_NUMBER, V_PLU_CODE,V_DSD_DATE
FROM DBMG_SAL_DETL DSD
WHERE DSD.CM_NUMBER = V_DSH_CM_NUMBER(indx);
--block1
/*Ln_Itm := 0;
FOR ind IN 1..V_DSD_CM_NUMBER.COUNT
loop
INSERT INTO PC_ALL_TAB
VALUES(V_DSH_CM_NUMBER(indx),
V_DSD_DATE(ind),
V_DSD_CM_NUMBER(ind),
V_PLU_CODE(ind),
LN_ITM,
str_id
LN_ITM := LN_ITM +1;
end loop; */
FORALL ind IN 1..V_DSD_CM_NUMBER.COUNT
INSERT INTO PC_ALL_TAB
VALUES(V_DSH_CM_NUMBER(indx),
V_DSD_DATE(ind),
V_DSD_CM_NUMBER(ind),
V_PLU_CODE(ind),
LN_ITM,
str_id
LN_ITM := LN_ITM +1;
--block2
END LOOP;
EXIT WHEN CUR_DBMG_SAL_HEAD%NOTFOUND;
END LOOP;
commit;
CLOSE CUR_DBMG_SAL_HEAD;
DBMS_OUTPUT.PUT_LINE('COMPLETE..!');
END ;
o/p :- SELECT DSH_CM_NUMBER, LN_ITM FROM PC_ALL_TAB;
DSH_CM_NUMBER LN_ITM
1 4177424 0
2 4177422 1
3 4177426 2
4 4177426 2
5 4177426 2
6 4177425 3
7 4177427 4
8 4177427 4
9 4177427 4
I need result as below...but using "FORALL"
DSH_CM_NUMBER LN_ITM
1 4177424 0
2 4177422 0
3 4177426 0
4 4177426 1
5 4177426 2
6 4177425 0
7 4177427 0
8 4177427 1
9 4177427 2Double post
How to increment value using "FORALL" instead of for loop -
Hi all
i have a problem.Actually i'm giving a test problem here.While executing this i'm getting ora 66550 error .What wrong i'm doing?
say col1 and col2 is column of tab1.
type test is table of number;
a_test test:=test(8,9);
cursor c1(test number) is
select col1 from tab1
where col2=test;
p1 c1%rowtype;
begin
forall i in 1 .. a_test.count
for p1 in c1(a_test(i)) loop
insert into b_test values(a_test(i),p1.col1)
end loop;
end;
Thanks in advancebp wrote:
Hi all
i have a problem.Actually i'm giving a test problem here.While executing this i'm getting ora 66550 error .What wrong i'm doing?
say col1 and col2 is column of tab1.
type test is table of number;
a_test test:=test(8,9);
cursor c1(test number) is
select col1 from tab1
where col2=test;
p1 c1%rowtype;
begin
forall i in 1 .. a_test.count
for p1 in c1(a_test(i)) loop
insert into b_test values(a_test(i),p1.col1)
end loop;
end;
test@ORA10G>
test@ORA10G>
test@ORA10G> select * from tab1;
C COL2
a 6
b 7
c 8
d 9
e 10
5 rows selected.
test@ORA10G>
test@ORA10G>
test@ORA10G> --
test@ORA10G> declare
2 type test is table of number;
3 a_test test:=test(8,9);
4 begin
5 forall i in 1 .. a_test.count
6 insert into b_test (x,y)
7 select col2, col1
8 from tab1
9 where col2 = a_test(i);
10 end;
11 /
PL/SQL procedure successfully completed.
test@ORA10G>
test@ORA10G> select * from b_test;
X Y
8 c
9 d
2 rows selected.
test@ORA10G>
test@ORA10G>isotope -
BULKING and FORALL statement does not pass values to non DML statements.
Hi
I've got million rows that i need to manapulate and insert into various procedures depending on the bussiness rule to be applied, but my dilemma is that the BULIKNG with a combination of a FORALL statement its not compatible(only considers straightforward DML calls) or the the FORALL does not like other SQL statements or calls as it prefers only DML's.
Below is code fragment that is problamatic as the compiler keeps sending me this error: PLS-00201: identifier 'INDX' must be declared
I want to manupulate data on the implicity cursor and call a procedure to do other stuff which don't use INSERT/DELETE or UPDATE statements.
Declare
TYPE tab_person_id is of table of number(15);
l_person_id tab_person_id;
BEGIN
SELECT person_id
BULK COLLECT INTO l_person_id
FROM person_details; /*-Million records-*/
FORALL indx IN l_tdtl.FIRST..l_tdtl.LAST
pj_pkg.ins_intf
(p_user => p_user,
p_typ_cd => '00',
p_person_id => l_person(indx)
Commit;
END;
How do I pass this value 'l_person(indx)' on the package procudure ?
Thanks
Amos@prabodh:
SQL> declare
2 TYPE tab_person_id is of table of number(15) index by pls_integer;
3 begin
4 null;
5 end;
6 /
TYPE tab_person_id is of table of number(15) index by pls_integer;
ERROR at line 2:
ORA-06550: line 2, column 23:
PLS-00103: Encountered the symbol "OF" when expecting one of the following:
( array limited new private range record VARRAY_ char_base
number_base decimal date_base clob_base blob_base bfile_base
table ref object fixed varying opaque sparse
The symbol "OF" was ignored.Check What you are posting.
@ qwestion: What is your Database Version? It is a implementation restriction.
Maybe you are looking for
-
Used Photoshop Image processor to batch convert from CR2 to Jpeg and the edits don't stick
Ok, so I had a huge wedding where the bride paid me to deliver a ton of images. Well, i did all my edits in ACR and then photoshop. While in photoshop all the edits look to have been applied. So, I use edit > Scripts>Image processor to convert the
-
Getting blue question mark for images in Safari
Hi guys I have MBP 15", early 2011, 250 GB SSD, 16 GB RAM, 10.10 Yosemite OSx and I am facing problem recently is that some images in Safari 8 browser won't open and instead blue question mark will arise . I have friend of mine using ipad2 and I aske
-
Remove Payment Method Country Specific
Hi Gurus, Is it possible to delete unnecessary Payment Methods in FBZP specific for one country? What are the impacts if this is removed if any? Any input is welcome Regards Roger
-
Hi, I have the back up of the enitre hyperion folder from my production env for a Planning application. I have replaced the Plan1 folder from the Prod env to the Dev env with the application I created in Essbase having a different name but the same d
-
Delivery document type settings
Hi all, In standard SAP For LF delivery type, Default order type DL (Delivery with out sales order reference) is assigned.What is the use of this. For delivery with out reference to sales order system will not allow us to use delivery type LF. we onl