Related
I have a procedure that does the INSERT INTO and then the UPDATE of some fields (both in the same procedure), I'm using this answer from #Clive Number of rows affected by an UPDATE in PL/SQLto know the amount of data that has been updated to put in a log, but it brings me the total number of rows instead of just the records that have been updated.
Is that the right way to know?
What I need is to know how many rows were INSERTED from the INSERT STATEMENT and how many rows were UPDATED from the UPDATE STATEMENT.
My query:
CREATE OR REPLACE PROCEDURE OWNER.TABLE_NAME
AS
-- VARIABLE
v_qtd_regs number := 0;
v_code number;
v_errm VARCHAR2(500);
start_time pls_integer;
end_time pls_integer;
elapse_time number;
proc_name varchar2(100);
i NUMBER;
BEGIN
proc_name := 'PRDWBI_CGA_D_COLUMNS';
start_time := dbms_utility.get_time;
DS_FUNCESP.PRDSBI_GRAVA_LOG( 'I', 'DataWarehouse', proc_name, 'Início Carga' );
-- INSERT INTO TABLE:
INSERT INTO OWNER.TABLE_NAME
(COLUMN_ID, COLUMNS_NAME, COLUMN_NAME2)
(SELECT 1 AS COLUMN_ID, 'TEST' AS COLUMN_NAME, SYSDATE AS COLUMN_NAME2 FROM DUAL);
COMMIT;
-- UPDATE SOME COLUMNS I NEED
UPDATE OWNER.TABLE_NAME y
SET (y.COLUMNS_NAME, y.COLUMN_NAME2) =
(SELECT 'TEST2' AS COLUMN_NAME, SYSDATE AS COLUMN_NAME2 FROM DUAL x WHERE x.COLUMN_ID = y.COLUMN_ID)
WHERE EXISTS (SELECT 'TEST2' AS COLUMN_NAME, SYSDATE AS COLUMN_NAME2 FROM DUAL x WHERE x.COLUMN_ID = y.COLUMN_ID);
-- TO KNOW HOW MANY ROWS WERE UPDATED
i := SQL%rowcount;
COMMIT;
--dbms_output.Put_line(i);
SELECT COUNT(1) INTO v_qtd_regs FROM OWNER.TABLE_NAME where LinData >= TRUNC(SYSDATE);
end_time := dbms_utility.get_time;
elapse_time := ((end_time - start_time)/100);
v_errm := SUBSTR(SQLERRM, 1 , 500);
DS_FUNCESP.PRDSBI_GRAVA_LOG('T', 'DataWarehouse', proc_name, v_errm, v_qtd_regs, elapse_time );
COMMIT;
EXCEPTION
WHEN OTHERS THEN
v_code := SQLCODE;
v_errm := SUBSTR(SQLERRM, 1 , 500);
DS_FUNCESP.PRDSBI_GRAVA_LOG('E', 'Error', proc_name, v_errm);
END;
QUESTION EDITED TO SHOW A REAL EXAMPLE:
I created a table that takes data from "SYS.DBA_TAB_COLUMNS" just to use as an example, as shown below:
CREATE TABLE "DW_FUNCESP"."D_TEST"
(
"ID_COLUMN" NUMBER(10,0) GENERATED BY DEFAULT ON NULL AS IDENTITY MINVALUE 1 MAXVALUE 9999999999999999999999999999 INCREMENT BY 1
START WITH 1 CACHE 20 NOORDER NOCYCLE NOKEEP NOSCALE NOT NULL ENABLE,
"NM_OWNER" VARCHAR2(500 CHAR) NOT NULL ENABLE ,
"NM_TABLE" VARCHAR2(500 CHAR) NOT NULL ENABLE ,
"CD_COLUMN" NUMBER(20,0) NOT NULL ENABLE ,
"NM_COLUMN" VARCHAR2(500 CHAR) NOT NULL ENABLE ,
"DS_COLUMN" VARCHAR2(500 CHAR) NOT NULL ENABLE ,
"LINDATE" DATE DEFAULT SYSDATE NOT NULL ENABLE ,
"LINORIGIN" VARCHAR2(100 CHAR) NOT NULL ENABLE
)
Then I created a procedure to identify the inserted and updated records, as below:
CREATE OR REPLACE PROCEDURE DW_FUNCESP.PRDWBI_CGA_D_TEST
AS
-- variaveis de suporte as informações que deve gravar
rows_inserted integer;
rows_updated integer;
BEGIN
-- Insert Into table
INSERT INTO DW_Funcesp.D_TEST
(NM_OWNER, NM_TABLE, CD_COLUMN, NM_COLUMN, DS_COLUMN, LINDATE, LINORIGIN)
(SELECT
NVL(x.NM_OWNER ,'NOT FOUND') AS NM_OWNER ,
NVL(x.NM_TABLE ,'NOT FOUND') AS NM_TABLE ,
NVL(x.CD_COLUMN ,-1) AS CD_COLUMN ,
NVL(x.NM_COLUMN ,'NOT FOUND') AS NM_COLUMN ,
NVL(x.DS_COLUMN ,x.NM_COLUMN) AS DS_COLUMN ,
SYSDATE AS LINDATE ,
'SYS.DBA_TAB_COLUMNS' AS LINORIGIN
FROM
(
SELECT
d.OWNER AS NM_OWNER ,
d.TABLE_NAME AS NM_TABLE ,
d.COLUMN_ID AS CD_COLUMN,
d.COLUMN_NAME AS NM_COLUMN,
e.COMMENTS AS DS_COLUMN
FROM SYS.DBA_TAB_COLUMNS d
LEFT JOIN SYS.DBA_COL_COMMENTS e
ON e.OWNER = d.OWNER
AND e.TABLE_NAME = d.TABLE_NAME
AND e.COLUMN_NAME = d.COLUMN_NAME
WHERE d.OWNER = 'DW_FUNCESP'
) x
LEFT JOIN DW_FUNCESP.D_TEST y
ON y.NM_OWNER = x.NM_OWNER
AND y.NM_TABLE = x.NM_TABLE
AND y.NM_COLUMN = x.NM_COLUMN
WHERE y.ID_COLUMN IS NULL);
rows_inserted := sql%rowcount;
-- Update the table
UPDATE DW_FUNCESP.D_TEST z
SET (z.NM_COLUMN, z.DS_COLUMN, z.LINDATE) =
(SELECT
NVL(x.NM_COLUMN ,'NOT FOUND') AS NM_COLUMN ,
NVL(x.DS_COLUMN ,x.NM_COLUMN) AS DS_COLUMN ,
SYSDATE AS LINDATE
FROM
(
SELECT
d.OWNER AS NM_OWNER ,
d.TABLE_NAME AS NM_TABLE ,
d.COLUMN_ID AS CD_COLUMN,
d.COLUMN_NAME AS NM_COLUMN,
e.COMMENTS AS DS_COLUMN
FROM SYS.DBA_TAB_COLUMNS d
LEFT JOIN SYS.DBA_COL_COMMENTS e
ON e.OWNER = d.OWNER
AND e.TABLE_NAME = d.TABLE_NAME
AND e.COLUMN_NAME = d.COLUMN_NAME
WHERE d.OWNER = 'DW_FUNCESP'
) x
WHERE z.NM_OWNER = x.NM_OWNER
AND z.NM_TABLE = x.NM_TABLE
AND z.CD_COLUMN = x.CD_COLUMN)
WHERE EXISTS (SELECT
NVL(x.NM_COLUMN ,'NOT FOUND') AS NM_COLUMN ,
NVL(x.DS_COLUMN ,x.NM_COLUMN) AS DS_COLUMN ,
SYSDATE AS LINDATE
FROM
(
SELECT
d.OWNER AS NM_OWNER ,
d.TABLE_NAME AS NM_TABLE ,
d.COLUMN_ID AS CD_COLUMN,
d.COLUMN_NAME AS NM_COLUMN,
e.COMMENTS AS DS_COLUMN
FROM SYS.DBA_TAB_COLUMNS d
LEFT JOIN SYS.DBA_COL_COMMENTS e
ON e.OWNER = d.OWNER
AND e.TABLE_NAME = d.TABLE_NAME
AND e.COLUMN_NAME = d.COLUMN_NAME
WHERE d.OWNER = 'DW_FUNCESP'
) x
WHERE z.NM_OWNER = x.NM_OWNER
AND z.NM_TABLE = x.NM_TABLE
AND z.CD_COLUMN = x.CD_COLUMN);
rows_updated := sql%rowcount;
dbms_output.Put_line('inserted=>' || to_char(rows_inserted) || ', updated=>' || to_char(rows_updated));
COMMIT;
EXCEPTION
WHEN OTHERS THEN
RAISE;
END;
So my first insert output was:
inserted=>2821, updated=>2821
So I chose a data to be changed and it was updated, I made the following select to choose which data should be updated to bring in the DBMS output again:
SELECT * FROM DW_FUNCESP.D_TEST WHERE NM_TABLE = 'D_TEST';
I commented in a column as shown in the image, to bring in the update:
COMMENT ON COLUMN DW_FUNCESP.D_TEST.LINORIGIN IS 'The origin of the data';
I ran the procedure again, and the output was:
inserted=>0, updated=>2821
The result for that update:
Shouldn't you have brought just 1 updated data in the output, as only 1 updated? And not all the rows?
e.g.: inserted=>0, updated=>1
So my question remains, am I asking it correctly? Is it possible to obtain this result in the same procedure? Is it the update that is incorrect (despite having updated the data)?
You are not getting the rows inserted and rows updated. SQL%rowcount contains ONLY the number rows from the last select or DML statement. Since you set your variable only after the Update your only get the number of updates. If you want both then you need a separate variable for each.
Hint: There is no need to commit after each DML, actually that is ofter considered a very poor practice. You need to study as bit on transactions. The basic idea being that all operations complete successfully or none of them complete successfully. Look up ATOMIC and Atomicity.
So your revised procedure becomes:
create or replace procedure owner.table_name
as
-- VARIABLE
v_qtd_regs number := 0;
v_code number;
v_errm varchar2(500);
start_time pls_integer;
end_time pls_integer;
elapse_time number;
proc_name varchar2(100);
rows_inserted integer;
rows_updated integer;
begin
proc_name := 'PRDWBI_CGA_D_COLUMNS';
start_time := dbms_utility.get_time;
ds_funcesp.prdsbi_grava_log( 'I', 'DataWarehouse', proc_name, 'Início Carga' );
insert into owner.table_name
(column_id, columns_name, column_name2)
(select 1 as column_id, 'TEST' as column_name, sysdate as column_name2 from dual);
rows_inserted := sql%rowcount;
update owner.table_name y
set (y.columns_name, y.column_name2) =
(select 'TEST2' as column_name, sysdate as column_name2 from dual x where x.column_id = y.column_id)
where exists (select 'TEST2' as column_name, sysdate as column_name2 from dual x where x.column_id = y.column_id);
rows_updated := sql%rowcount;
dbms_output.Put_line('inserted=>' || to_char(rows_inserted) || ', updated=>' || tp_char(rows_updated));
select count(1) into v_qtd_regs from owner.table_name where lindata >= trunc(sysdate);
end_time := dbms_utility.get_time;
elapse_time := ((end_time - start_time)/100);
v_errm := substr(sqlerrm, 1 , 500);
ds_funcesp.prdsbi_grava_log('T', 'DataWarehouse', proc_name, v_errm, v_qtd_regs, elapse_time );
commit;
exception
when others then
v_code := sqlcode;
v_errm := substr(sqlerrm, 1 , 500);
ds_funcesp.prdsbi_grava_log('E', 'Error', proc_name, v_errm);
end;
Try to add the instruction i := SQL%rowcount; after each DML:
after INSERT to have the number of inserted rows
after UPDATE to have the number of updated rows
I would use ORA_SCN as the other answers suggest if you are interested what rows have been inserted or updated. But you want only to know how many, so I would leave the counting to Oracle (might be timeconsuming for larger number of rows).
Please have a look at the data dictionary view USER_TAB_MODIFICATIONS (or ALL_TAB_MODIFICATIONS if the table is in another schema than the procedure.
CREATE TABLE d (
id NUMBER GENERATED ALWAYS AS IDENTITY,
dt DATE DEFAULT SYSDATE,
foo VARCHAR2(128 BYTE)
);
Gathering the table statistics will reset the modifications view:
EXEC DBMS_STATS.GATHER_TABLE_STATS(NULL,'D');
Now after your INSERT, the modifications view will have the number of inserted rows:
INSERT INTO d(foo) SELECT object_name FROM all_objects;
67,141 rows inserted.
SELECT inserts, updates, deletes FROM user_tab_modifications WHERE table_name='D';
INSERTS UPDATES DELETES
67141 0 0
Likewise, after the UPDATE, the updated rows:
UPDATE d SET foo=lower(foo),dt=SYSDATE WHERE mod(id,10)=0;
6,714 rows updated.
SELECT inserts, updates, deletes FROM user_tab_modifications WHERE table_name='D';
INSERTS UPDATES DELETES
67141 6714 0
For clarity, I've used SQL instead of PL/SQL. You might have to grant some special privs to the schema containing the procedure. Add a comment with my name if you run into problems with that.
When trying to compile in order to execute both functions(#directory) I keep getting a no data error on line 48. (or line 13 for the function). This is the error i receive. NOTE: SYSTEM is the server I'm connected to.
ERROR at line 1:
ORA-01403: no data found
ORA-06512: at "SYSTEM.DEPTPROJECT", line 13
ORA-06512: at line 4
ORA-06512: at line 4
these are my function
SET ECHO ON;
SET FEEDBACK ON;
SET LINESIZE 100;
SET PAGESIZE 100;
SET SERVEROUTPUT ON;
CREATE OR REPLACE FUNCTION DEPTPROJECT(departmentNumber IN NUMBER)
RETURN VARCHAR IS
dep# DEPARTMENT.D#%TYPE;
depName DEPARTMENT.DName%TYPE;
counter NUMBER(10,0);
empNumber CHAR(5);
empName VARCHAR2(30);
result VARCHAR2(600);
BEGIN
SELECT D#, DNAME INTO dep#, depName FROM DEPARTMENT WHERE DEPARTMENT.D# = dep#;
result := result || 'Department'|| dep# || '' || depName || chr (10);
FOR i IN(SELECT P# , PTitle , Budget FROM PROJECT WHERE PROJECT.D# = dep# ORDER BY BUDGET DESC)
LOOP
result:= result || chr(9)|| 'Project: ' || i.P# || '' || i.PTitle || i.Budget || chr(10);
FOR j IN(SELECT EMPLOYEE.Name, EMPLOYEE.E# FROM WORKSON INNER JOIN EMPLOYEE ON EMPLOYEE.E# = WORKSON.E# WHERE WORKSON.P# = i.P# ORDER BY EMPLOYEE.NAME ASC )
LOOP
result:= result || chr(10) || j.E# || '' || j.Name || chr(10);
END LOOP;
END LOOP;
RETURN result;
END;
/
BEGIN
FOR x IN(SELECT D# FROM DEPARTMENT)
LOOP
DBMS_OUTPUT.PUT_LINE(DEPTPROJECT(x.D#));
END LOOP;
END;
/
When you are selecting INTO a variable and there are no records returned you should get a NO DATA FOUND error. I believe the correct way to write the above code would be to wrap the SELECT statement with it's own BEGIN/EXCEPTION/END block.
Example:
SET ECHO ON;
SET FEEDBACK ON;
SET LINESIZE 100;
SET PAGESIZE 100;
SET SERVEROUTPUT ON;
CREATE OR REPLACE FUNCTION DEPTPROJECT(departmentNumber IN NUMBER)
RETURN VARCHAR IS
dep# DEPARTMENT.D#%TYPE;
depName DEPARTMENT.DName%TYPE;
counter NUMBER(10,0);
empNumber CHAR(5);
empName VARCHAR2(30);
result VARCHAR2(600);
BEGIN
SELECT D#, DNAME INTO dep#, depName FROM DEPARTMENT WHERE DEPARTMENT.D# = departmentNumber;
result := result || 'Department'|| dep# || '' || depName || chr (10);
FOR i IN(SELECT P# , PTitle , Budget FROM PROJECT WHERE PROJECT.D# = dep# ORDER BY BUDGET DESC)
LOOP
result:= result || chr(9)|| 'Project: ' || i.P# || '' || i.PTitle || i.Budget || chr(10);
FOR j IN(SELECT EMPLOYEE.Name, EMPLOYEE.E# FROM WORKSON INNER JOIN EMPLOYEE ON EMPLOYEE.E# = WORKSON.E# WHERE WORKSON.P# = i.P# ORDER BY EMPLOYEE.NAME ASC )
LOOP
result:= result || chr(10) || j.E# || '' || j.Name || chr(10);
END LOOP;
END LOOP;
EXCEPTION
WHEN NO_DATA_FOUND THEN
result := 'Record Not Found';
RETURN result;
END;
/
BEGIN
FOR x IN(SELECT D# FROM DEPARTMENT)
LOOP
DBMS_OUTPUT.PUT_LINE(DEPTPROJECT(x.D#));
END LOOP;
END;
/
I'd guess that line 13 should use the variable being passed to the function:
SELECT D#, DNAME INTO dep#, depName FROM DEPARTMENT WHERE DEPARTMENT.D# = dep#;
Should be:
SELECT D#, DNAME INTO dep#, depName FROM DEPARTMENT WHERE DEPARTMENT.D# = departmentNumber;
You are using following query to fetch dep# and depName but your where clause is WHERE DEPARTMENT.D# = dep# .
SELECT D#, DNAME INTO dep#, depName FROM DEPARTMENT WHERE DEPARTMENT.D# = dep#;
Here dep# is a variable which you have declared but not initialized.
So dep# is null and comparision to NULL will always lead to false which means your query is returning no record.
As you have used INTO in your query, Oracle is seeking for exactly one record from this query but it is returning no records. Hence, you are facing the issue.
According to your logic either change the WHERE clause to include departmentNumber in comparision or assign proper value to dep# before using it in your query.
Also, make sure to wrap your SELECT .. INTO query in BEGIN..EXCEPTION..END to avoid such a dynamic exception.
Cheers!!
I was trying in my stored procedure in Oracle to compare two numbers (both extracted from a DATE with EXTRACT function) with 2 more numbers (extracted with EXTRACT), and I wanted to check if I already have a row with the same month and year, but no matter what I do, I always find a row EVEN THO THE MONTH AND THE YEAR IS DIFFERENT!
Here's my code below that I tested outside my stored procedure, and the DBMS_OUTPUT always prints 1 occurence found in my SELECT COUNT(*):
DECLARE
VAR_COUNTER NUMBER;
VAR_MONTH NUMBER;
VAR_YEAR NUMBER;
BEGIN
SELECT EXTRACT(MONTH FROM TO_DATE('10/12/2019','DD/MM/YYYY')),EXTRACT(YEAR FROM TO_DATE('10/12/2019','DD/MM/YYYY'))
INTO VAR_MONTH,VAR_YEAR FROM DUAL;
SELECT COUNT(*)
INTO VAR_COUNTER
FROM FATTURATO_RISTORANTI
WHERE FATTURATO_RISTORANTI.ID_RISTORANTE = 3
AND FATTURATO_RISTORANTI.MESE= VAR_MONTH
AND FATTURATO_RISTORANTI.ANNO = VAR_YEAR;
DBMS_OUTPUT.PUT_LINE(VAR_MONTH || ' ' || VAR_YEAR || ' ' || VAR_COUNTER);
END;
P.S. : mese is the extracted MONTH and anno is the extracted YEAR;
If you need the values only once you can also write
DECLARE
contatore NUMBER;
BEGIN
SELECT COUNT(*)
INTO contatore
FROM FATTURATO_RISTORANTI
WHERE FATTURATO_RISTORANTI.ID_RISTORANTE = 3
AND FATTURATO_RISTORANTI.MESE = EXTRACT(MONTH FROM DATE '2019-12-10')
AND FATTURATO_RISTORANTI.ANNO = EXTRACT(YEAR FROM DATE '2019-12-10');
DBMS_OUTPUT.PUT_LINE(contatore);
END;
Or use
vMese := EXTRACT(MONTH FROM TO_DATE('10/12/2019', 'DD/MM/YYYY'));
vAnno := EXTRACT(YEAR FROM TO_DATE('10/12/2019', 'DD/MM/YYYY'));
FATTURATO_RISTORANTI.ID_RISTORANTE = 3 is equal to 3 = FATTURATO_RISTORANTI.ID_RISTORANTE but more "common readable".
This should be like:
DECLARE
contatore NUMBER;
vMese NUMBER;
vAnno NUMBER;
BEGIN
SELECT EXTRACT(MONTH FROM TO_DATE('10/12/2019', 'DD/MM/YYYY')), EXTRACT(YEAR FROM TO_DATE('10/12/2019', 'DD/MM/YYYY'))
INTO vMese, vAnno
FROM DUAL;
SELECT COUNT(*)
INTO contatore
FROM FATTURATO_RISTORANTI
WHERE 3 = FATTURATO_RISTORANTI.ID_RISTORANTE
AND FATTURATO_RISTORANTI.MESE = vMese
AND FATTURATO_RISTORANTI.ANNO = vAnno;
DBMS_OUTPUT.PUT_LINE(vMese || ' ' || vAnno || ' ' || contatore);
END;
I apparently solved my problem by NOT using a variable, instead of using mese and anno, I just gave the parameter that the table needed, I still don't know why it works like this, I need to investigate!
I have migrated below procedure MYSQL to ORACLE and Migrated version is also given below.
Migrated procedure has been successfully complied but when we run this procedure in SQL Developer with respective parameter to print the "v_refcur" SYS_REFCURSOR result
It gives me ERROR: ORA-29481: Implicit results cannot be returned to client
--- Source Mysql Procedure-----------------------------------------
DROP PROCEDURE IF EXISTS `get_police_station_for_hierarchy` ;
DELIMITER $$
CREATE PROCEDURE `get_police_station_for_hierarchy`(officecd BIGINT,pstsaffcd VARCHAR(20))
BEGIN
DECLARE table_count INT;
DECLARE officerInCharge VARCHAR(20);
DROP TEMPORARY TABLE IF EXISTS temp1;
DROP TEMPORARY TABLE IF EXISTS temp2;
DROP TEMPORARY TABLE IF EXISTS temp3;
DROP TEMPORARY TABLE IF EXISTS temp4;
CREATE TEMPORARY TABLE temp1 (cd BIGINT,off_level INT);
CREATE TEMPORARY TABLE temp2 (cd BIGINT,off_level INT);
CREATE TEMPORARY TABLE temp3 (cd BIGINT,off_level INT);
CREATE TEMPORARY TABLE temp4 (cd BIGINT,off_level INT);
SELECT OFFICE_INCHARGE_CD INTO officerInCharge FROM m_office_types WHERE OFFICE_CD=officecd ;
IF(officerInCharge=pstsaffcd) THEN
INSERT INTO temp1
SELECT m_office_hierarchy.OFFICE_CD,m_office_types.OFFICE_TYPE_CD FROM m_office_hierarchy,m_office_types WHERE SUPERVISOR_OFFICE_CD =officecd
AND m_office_hierarchy.office_cd=m_office_types.office_cd AND m_office_hierarchy.lang_cd=m_office_types.lang_cd AND m_office_hierarchy.RECORD_STATUS <> 'D'
AND m_office_types.RECORD_STATUS <> 'D' AND m_office_types.OFFICE_TYPE_CD=7;
INSERT INTO temp2
SELECT m_office_hierarchy.OFFICE_CD,m_office_types.OFFICE_TYPE_CD FROM m_office_hierarchy,m_office_types WHERE SUPERVISOR_OFFICE_CD=officecd
AND m_office_hierarchy.office_cd=m_office_types.office_cd AND m_office_hierarchy.lang_cd=m_office_types.lang_cd
AND m_office_hierarchy.RECORD_STATUS <> 'D' AND m_office_types.RECORD_STATUS <> 'D' AND m_office_types.OFFICE_TYPE_CD <> 7 ;
SELECT COUNT(*) INTO table_count FROM temp2;
WHILE (table_count>0)
DO
INSERT INTO temp1
SELECT m_office_hierarchy.OFFICE_CD,m_office_types.OFFICE_TYPE_CD FROM m_office_hierarchy,m_office_types WHERE SUPERVISOR_OFFICE_CD IN (SELECT cd FROM temp2)
AND m_office_hierarchy.office_cd=m_office_types.office_cd AND m_office_hierarchy.lang_cd=m_office_types.lang_cd
AND m_office_hierarchy.RECORD_STATUS <> 'D' AND m_office_types.RECORD_STATUS <> 'D' AND m_office_types.OFFICE_TYPE_CD=7;
INSERT INTO temp3
SELECT m_office_hierarchy.OFFICE_CD,m_office_types.OFFICE_TYPE_CD FROM m_office_hierarchy,m_office_types WHERE SUPERVISOR_OFFICE_CD IN (SELECT cd FROM temp2)
AND m_office_hierarchy.office_cd=m_office_types.office_cd AND m_office_hierarchy.lang_cd=m_office_types.lang_cd
AND m_office_hierarchy.RECORD_STATUS <> 'D' AND m_office_types.RECORD_STATUS <> 'D' AND m_office_types.OFFICE_TYPE_CD <> 7 AND m_office_hierarchy.OFFICE_CD NOT IN (SELECT cd FROM temp4) AND m_office_hierarchy.OFFICE_CD <> officecd ;
DELETE FROM temp2;
INSERT INTO temp2 SELECT * FROM temp3;
INSERT INTO temp4 SELECT * FROM temp3;
DELETE FROM temp3;
SELECT COUNT(*) INTO table_count FROM temp2;
END WHILE;
SELECT DISTINCT cd,M_OFFICE_TYPES.office_name FROM temp1,m_office_types WHERE temp1.cd=m_office_types.office_cd AND m_office_types.record_status <> 'D';
ELSE
SELECT '' ;
END IF ;
DROP TABLE temp1;
DROP TABLE temp2;
DROP TABLE temp3;
DROP TABLE temp4;
END $$
DELIMITER ;
--- Migrated Oracle Procedure ----------------------------------------
create or replace PROCEDURE get_police_statn_for_hierarchy(officecd IN NUMBER,pstsaffcd IN VARCHAR2)
as
v_refcur SYS_REFCURSOR;
table_count NUMBER(10,0);
officerInCharge VARCHAR2(20);
BEGIN
-- This procedure was converted on Mon Nov 14 14:57:57 2016 using Ispirer SQLWays 7.0 Build 3434 64bit Licensed to prabhat.gang#gmail.com - prabhat - India (Demo License, Ispirer MnMTK 2015 Mysql to Oracle Database Migration, 1 month, 20161114).
EXECUTE IMMEDIATE ' TRUNCATE TABLE temp1 ';
EXECUTE IMMEDIATE ' TRUNCATE TABLE temp2 ';
EXECUTE IMMEDIATE ' TRUNCATE TABLE temp3 ';
INSERT INTO temp1
SELECT m_office_hierarchy.OFFICE_CD,m_office_types.OFFICE_TYPE_CD FROM m_office_hierarchy,m_office_types WHERE SUPERVISOR_OFFICE_CD = officecd
and m_office_hierarchy.office_cd = m_office_types.office_cd and m_office_hierarchy.lang_cd = m_office_types.lang_cd and m_office_hierarchy.RECORD_STATUS <> 'D'
and m_office_types.RECORD_STATUS <> 'D' and m_office_types.OFFICE_TYPE_CD = 7;
INSERT INTO temp2
SELECT m_office_hierarchy.OFFICE_CD,m_office_types.OFFICE_TYPE_CD FROM m_office_hierarchy,m_office_types WHERE SUPERVISOR_OFFICE_CD = officecd
and m_office_hierarchy.office_cd = m_office_types.office_cd and m_office_hierarchy.lang_cd = m_office_types.lang_cd
and m_office_hierarchy.RECORD_STATUS <> 'D' and m_office_types.RECORD_STATUS <> 'D' and m_office_types.OFFICE_TYPE_CD <> 7;
SELECT COUNT(*) INTO table_count FROM temp2;
WHILE (table_count > 0) loop
INSERT INTO temp1
SELECT m_office_hierarchy.OFFICE_CD,m_office_types.OFFICE_TYPE_CD FROM m_office_hierarchy,m_office_types WHERE SUPERVISOR_OFFICE_CD IN(SELECT cd FROM temp2)
and m_office_hierarchy.office_cd = m_office_types.office_cd and m_office_hierarchy.lang_cd = m_office_types.lang_cd
and m_office_hierarchy.RECORD_STATUS <> 'D' and m_office_types.RECORD_STATUS <> 'D' and m_office_types.OFFICE_TYPE_CD = 7;
INSERT INTO temp3
SELECT m_office_hierarchy.OFFICE_CD,m_office_types.OFFICE_TYPE_CD FROM m_office_hierarchy,m_office_types WHERE SUPERVISOR_OFFICE_CD IN(SELECT cd FROM temp2)
and m_office_hierarchy.office_cd = m_office_types.office_cd and m_office_hierarchy.lang_cd = m_office_types.lang_cd
and m_office_hierarchy.RECORD_STATUS <> 'D' and m_office_types.RECORD_STATUS <> 'D' and m_office_types.OFFICE_TYPE_CD <> 7;
DELETE FROM temp2;
INSERT INTO temp2 SELECT * FROM temp3;
DELETE FROM temp3;
SELECT COUNT(*) INTO table_count FROM temp2;
table_count := 100;
end loop;
open v_refcur for SELECT DISTINCT cd,M_OFFICE_TYPES.office_name FROM temp1,m_office_types WHERE temp1.cd = m_office_types.office_cd and m_office_types.record_status <> 'D';
dbms_sql.return_result(v_refcur);
EXECUTE IMMEDIATE ' TRUNCATE TABLE temp1 ';
EXECUTE IMMEDIATE ' TRUNCATE TABLE temp2 ';
EXECUTE IMMEDIATE ' TRUNCATE TABLE temp3 ';
close v_refcur;
END;
Complete Error log While ruining above procedure:
ORA-29481: Implicit results cannot be returned to client
ORA-06512: at "SYS.DBMS_SQL", line 2785
ORA-06512: at "SYS.DBMS_SQL", line 2779
ORA-06512: at "CAS_USER.GET_POLICE_STATN_FOR_HIERARCHY", line 38
ORA-06512: at line 8
I am new in oracle world, Please help me to resolve above issue so that i can run above procedure to see "v_refcur" SYS_REFCURSOR output OR Result cursor in SQL Developer.
This can be due to your client driver not being updated to 12c. E.g. when calling the stored procedure from Java, make sure you have the latest 12c JDBC driver, not the 11g one.
I've also documented this in a separate Q&A in case this problem here is not related to JDBC.
Below is stored procedure I have written which used nested cursor.
create or replace
PROCEDURE SP_RUN_EMPLOYEE_UPDATES
(
IN_DATE IN VARCHAr2
)
IS
update_sql varchar2(4000);
employee_id BI_EMPLOYEE_UPDATE.employee_id%TYPE;
effective_date date ;
created_by number;
created_on date;
comments varchar2(4000);
CURSOR
employees
IS
SELECT distinct(employee_id) FROM BI_EMPLOYEE_UPDATE WHERE EFFECTIVE_DATE = to_date(IN_DATE,'dd-mm-yy') AND EXECUTED = 'N' AND ACTIVITY_ID = '0';
CURSOR
e_updates
IS
SELECT * FROM BI_EMPLOYEE_UPDATE WHERE EFFECTIVE_DATE = to_date(IN_DATE,'dd-mm-yy') AND EXECUTED = 'N' AND ACTIVITY_ID = '0' and employee_id = employee_id ;
BEGIN
OPEN employees;
LOOP
effective_date := '';
created_by := '';
created_on := '';
comments := '';
employee_id := '';
FETCH employees into employee_id;
EXIT WHEN employees%NOTFOUND;
update_sql := 'UPDATE BI_EMPLOYEE SET ';
FOR e_update in e_updates
LOOP
select comments, effective_date , changed_by, changed_on into comments, effective_date , created_by, created_on
from bi_employee_update where EMPLOYEE_UPDATE_ID = e_update.EMPLOYEE_UPDATE_ID;
update_sql := update_sql || e_update.column_name || ' = ''' || e_update.new_value || ''' , ' ;
UPDATE BI_EMPLOYEE_UPDATE
SET
EXECUTED = 'Y'
WHERE
EMPLOYEE_UPDATE_ID = e_update.EMPLOYEE_UPDATE_ID ;
END LOOP;
update_sql := update_sql || ' comments = ''' || comments || ''', updated_by = ''' || created_by || ''', updated_on = ''' || created_on || ''', effective_date = ''' || effective_date || '''';
update_sql := update_sql || ' WHERE emp_id = ' || employee_id ;
dbms_output.put_line('KKKK '||update_sql);
execute immediate update_sql ;
END LOOP;
CLOSE employees;
END;
The problem is in the second cursor where I get the data of all the previous cursors combined.
e.g. if first iteration shoud return a, second should return b. But in actual first iteration returns a, b and second also returns a,b.
Below is the dynamic query generated which is exactly same.
1st iteration
EXPECTED (CORRECT):
UPDATE BI_EMPLOYEE SET EMPLOYEE_ID = '1111111111111' , PP_NUMBER = '22222222222' ,
CORPORATE_TITLE_ID = '2' , comments = 'c11', updated_by = '361',
updated_on = '12-SEP-12', effective_date = '25-SEP-12' WHERE emp_id = 18010
ACTUAL (WRONG):
UPDATE BI_EMPLOYEE SET EMPLOYEE_ID = '1111111111111' , PP_NUMBER = '22222222222' ,
CORPORATE_TITLE_ID = '2' , LASTNAME = 'Ll22 edited ' , OFFSHORE_ONSHORE = '1' ,
ONSHORE_REGION = '1' , ONSHORE_DESK_MANAGER = 'henrry ' ,
comments = 'cc 33 33', updated_by = '361', updated_on = '12-SEP-12',
effective_date = '25-SEP-12' WHERE emp_id = 18010
2nd iteration
EXPECTED (CORRECT):
UPDATE BI_EMPLOYEE SET LASTNAME = 'Ll22 edited ' , OFFSHORE_ONSHORE = '1' ,
ONSHORE_REGION = '1' , ONSHORE_DESK_MANAGER = 'henrry ' ,
comments = 'cc 33 33', updated_by = '361', updated_on = '12-SEP-12',
effective_date = '25-SEP-12' WHERE emp_id = 18009
ACTUAL (WRONG):
UPDATE BI_EMPLOYEE SET EMPLOYEE_ID = '1111111111111' , PP_NUMBER = '22222222222' ,
CORPORATE_TITLE_ID = '2' , LASTNAME = 'Ll22 edited ' ,
OFFSHORE_ONSHORE = '1' , ONSHORE_REGION = '1' ,
ONSHORE_DESK_MANAGER = 'henrry ' , comments = 'cc 33 33',
updated_by = '361', updated_on = '12-SEP-12',
effective_date = '25-SEP-12'
WHERE emp_id = 18009
Why is this happening?
As mentioned in a comment on your previous question, your second cursor is not restricted to the employee found by the first cursor because you have no link between them. Where you have:
and employee_id = employee_id
... both of those refer to the table column so it doesn't act as a filter at all. You've given your local variable the same name, which confuses things enough, but it's out of scope anyway - this cursor has no visibility of the variable value set in the main body of the procedure.
You need to do something like:
CREATE OR REPLACE PROCEDURE sp_run_employee_updates (p_date IN DATE) IS
update_sql varchar2(4000);
first_update boolean;
CURSOR c_employees IS
SELECT DISTINCT employee_id
FROM bi_employee_update
WHERE effective_date = p_date
AND executed = 'N'
AND activity_id = '0';
CURSOR c_updates(cp_employee_id bi_employee_update.employee_id%TYPE) IS
SELECT *
FROM bi_employee_update
WHERE effective_date = p_date
AND executed = 'N'
AND activity_id = '0'
AND employee_id = cp_employee_id
FOR UPDATE;
BEGIN
-- loop around all employees with pending records
FOR r_employee IN c_employees LOOP
-- reset the update_sql variable to its base
update_sql := 'UPDATE BI_EMPLOYEE SET ';
-- reset the flag so we only add the comments etc. on the first record
first_update := true;
-- loop around all pending records for this employee
FOR r_update IN c_updates(r_employee.employee_id) LOOP
-- add the comments etc., only for the first update we see
if first_update then
update_sql := update_sql
|| ' comments = ''' || r_update.comments || ''','
|| ' updated_by = ''' || r_update.changed_by || ''','
|| ' updated_on = ''' || r_update.changed_on || ''','
|| ' effective_date = ''' || r_update.effective_date || '''';
first_update := false;
end if;
-- add the field/value from this record to the variable
update_sql := update_sql || ', '
|| r_update.column_name || ' = ''' || r_update.new_value || '''' ;
-- mark this update as executed
UPDATE bi_employee_update
SET executed = 'Y'
WHERE CURRENT OF c_updates;
END LOOP;
-- apply this update to the bi_employee record
update_sql := update_sql || ' WHERE emp_id = ' || r_employee.employee_id;
DBMS_OUTPUT.PUT_LINE(update_sql);
EXECUTE IMMEDIATE update_sql;
END LOOP;
END sp_run_employee_updates;
The important difference, really, is that the second cursor now has a parameter, and the employee ID from the first cursor is passed as that parameter.
Also, IN_DATE is declared as a date, so you don't need to pass it through TO_DATE(). There are going to be implicit date conversions in other places (effective dates etc.) because you're treating them as strings, but as long as they don't have time components this probably won't break anything as it should be consistent within the procedure.