SQL Developer will not insert data - oracle

create or replace PROCEDURE ADD_TO_BLACKLIST(
P_EMPLOYEE_USERNAME IN VARCHAR2,
T_CURSOR OUT SYS_REFCURSOR
)
AS
BEGIN
DECLARE
E_COUNT PLS_INTEGER := 0;
BEGIN
SELECT COUNT(*) INTO E_COUNT FROM EXAMPLE_TABLE
WHERE UPPER(EMPLOYEE_USERNAME) LIKE UPPER(P_EMPLOYEE_USERNAME)||'%';
IF E_COUNT = 0 THEN
INSERT INTO EXAMPLE_TABLE
(employee_number, employee_username)
SELECT EMPLOYEE_NUMBER, EMAIL FROM EXAMPLE_VIEW
WHERE UPPER(EMAIL)=CONCAT(UPPER(P_EMPLOYEE_USERNAME), '#microsoft.com');
ELSE
UPDATE EXAMPLE_TABLE
SET (EMPLOYEE_NUMBER, EMPLOYEE_USERNAME) =
(SELECT EMPLOYEE_NUMBER, EMAIL FROM EXAMPLE_VIEW
WHERE UPPER(EMAIL) = CONCAT(UPPER(P_EMPLOYEE_USERNAME), '#microsoft.com'));
COMMIT;
END IF;
OPEN T_CURSOR For
SELECT * FROM EXAMPLE_VIEW
WHERE EMAIL LIKE CONCAT(UPPER(P_EMPLOYEE_USERNAME), '%');
END;
END ADD_TO_BLACKLIST;
This compiles, but when I try to test it with a valid P_EMPLOYEE_USERNAME (which I've confirmed to be in the EXAMPLE_VIEW), I do not see any data being inserted.
I am new to PLSQL and not sure how to figure out the value of E_COUNT
The Example_Table DDL is
CREATE TABLE "Example_Table"
( "EMPLOYEE_NUMBER" NUMBER NOT NULL ENABLE,
"EMPLOYEE_USERNAME" VARCHAR2(250 BYTE) NOT NULL ENABLE,
"ACCOUNT_STATUS" NUMBER DEFAULT 0,
"ACCOUNT_STATUS_LAST_UPDATE" TIMESTAMP (6) DEFAULT SYSDATE NOT NULL ENABLE,
CONSTRAINT "BOE_SAFEGAURD_PK" PRIMARY KEY ("EMPLOYEE_USERNAME"))

The issue is in below line,you are not converting the case after concatenation.please modify and try below,
WHERE UPPER(EMAIL) = UPPER(CONCAT(UPPER(P_EMPLOYEE_USERNAME), '#microsoft.com'));
EDIT : To prove the theory please find below the details.
I have tested this and it works,
DDL to create the tables:
CREATE TABLE Example_Table
( EMPLOYEE_NUMBER NUMBER NOT NULL ENABLE,
EMPLOYEE_USERNAME VARCHAR2(250 BYTE) NOT NULL ENABLE,
ACCOUNT_STATUS NUMBER DEFAULT 0,
ACCOUNT_STATUS_LAST_UPDATE TIMESTAMP (6) DEFAULT SYSDATE NOT NULL ENABLE,
CONSTRAINT BOE_SAFEGAURD_PK PRIMARY KEY (EMPLOYEE_USERNAME));
CREATE TABLE Example_view
( EMPLOYEE_NUMBER NUMBER NOT NULL ENABLE,
EMAIL VARCHAR2(250 BYTE) NOT NULL ENABLE,
ACCOUNT_STATUS NUMBER DEFAULT 0,
ACCOUNT_STATUS_LAST_UPDATE TIMESTAMP (6) DEFAULT SYSDATE NOT NULL ENABLE
);
DML to populate data to example_view that will be used for the test.
insert into example_view values(1,'Test#microsoft.com',1,sysdate);
Modified the procedure to add UPPER on the rightside of the join for both insert and update conditions and place the commit after end if.A good code should have only one commit and that should be at the end of execution before the exception block of main begin..end block.
create or replace PROCEDURE ADD_TO_BLACKLIST(
P_EMPLOYEE_USERNAME IN VARCHAR2,
T_CURSOR OUT SYS_REFCURSOR
)
AS
BEGIN
DECLARE E_COUNT PLS_INTEGER := 0;
BEGIN
SELECT COUNT(*) INTO E_COUNT FROM EXAMPLE_TABLE WHERE UPPER(EMPLOYEE_USERNAME) LIKE UPPER(P_EMPLOYEE_USERNAME)||'%';
IF E_COUNT = 0 THEN
INSERT INTO EXAMPLE_TABLE
(employee_number, employee_username)
SELECT EMPLOYEE_NUMBER, EMAIL FROM EXAMPLE_VIEW WHERE UPPER(EMAIL)=UPPER(CONCAT(UPPER(P_EMPLOYEE_USERNAME), '#microsoft.com'));
ELSE
UPDATE EXAMPLE_TABLE SET (EMPLOYEE_NUMBER, EMPLOYEE_USERNAME) = (SELECT EMPLOYEE_NUMBER, EMAIL FROM EXAMPLE_VIEW WHERE UPPER(EMAIL)=UPPER(CONCAT(UPPER(P_EMPLOYEE_USERNAME), '#microsoft.com')));
END IF;
COMMIT;
OPEN T_CURSOR For
SELECT * FROM EXAMPLE_VIEW WHERE EMAIL LIKE CONCAT(UPPER(P_EMPLOYEE_USERNAME), '%');
END;
END ADD_TO_BLACKLIST;
In an anonymous block invoked the procedure,
DECLARE
T_CURSOR SYS_REFCURSOR;
BEGIN
ADD_TO_BLACKLIST('test',T_CURSOR);
end;
Ran a query to check if records are inserted,
select * from example_table;
Output is below,

You just need a commit after IF-ELSE condition rather than inside it. I have updated your code along with some other minor updates -
create or replace PROCEDURE ADD_TO_BLACKLIST( P_EMPLOYEE_USERNAME IN VARCHAR2,
T_CURSOR OUT SYS_REFCURSOR
)
AS
E_COUNT PLS_INTEGER := 0;
BEGIN
SELECT COUNT(*)
INTO E_COUNT
FROM EXAMPLE_TABLE
WHERE UPPER(EMPLOYEE_USERNAME) LIKE UPPER(P_EMPLOYEE_USERNAME)||'%';
IF E_COUNT = 0 THEN
INSERT INTO EXAMPLE_TABLE
(employee_number, employee_username)
SELECT EMPLOYEE_NUMBER, EMAIL
FROM EXAMPLE_VIEW
WHERE UPPER(EMAIL) = CONCAT(UPPER(P_EMPLOYEE_USERNAME), '#microsoft.com');
ELSE
UPDATE EXAMPLE_TABLE
SET (EMPLOYEE_NUMBER, EMPLOYEE_USERNAME) = (SELECT EMPLOYEE_NUMBER, EMAIL
FROM EXAMPLE_VIEW
WHERE UPPER(EMAIL)=CONCAT(UPPER(P_EMPLOYEE_USERNAME), '#microsoft.com'));
END IF;
COMMIT;
OPEN T_CURSOR For
SELECT *
FROM EXAMPLE_VIEW
WHERE EMAIL LIKE CONCAT(UPPER(P_EMPLOYEE_USERNAME), '%');
END ADD_TO_BLACKLIST;

Related

Need to accept comma separated inputs from the stored procedure and have to process as I have explained in the below body but getting compilation err

CREATE TABLE STAGING
(
E_ID NUMBER(10),
E_NAME VARCHAR2(30),
E_LOC VARCHAR2(30),
VALIDATION_STATUS varchar2(30),
validation_msg varchar2(30),
req_id number(10)
);
INSERT INTO staging VALUES(1, 'A', 'AA', NULL, NULL, 1);
INSERT INTO staging VALUES(2, 'B', 'BB', NULL, NULL, 1);
INSERT INTO staging VALUES(3, 'C', 'CC', NULL, NULL, 1);
INSERT INTO staging VALUES(NULL, 'D', 'DD', NULL, NULL, 2);
INSERT INTO staging VALUES(NULL, 'E', 'EE', NULL, NULL, 2);
INSERT INTO staging VALUES(NULL, 'F', 'GG', NULL, NULL, 2);
CREATE TABLE tab_ref
(
ref_id number(10),
ref_name varchar2(30)
);
INSERT INTO tab_ref VALUES(1, 'aa');
INSERT INTO tab_ref VALUES(2, 'bb');
INSERT INTO tab_ref VALUES(3, 'cc');
INSERT INTO tab_ref VALUES(4, 'dd');
CREATE TABLE tab_ref_2
(
ref_id number(10),
ref_name varchar2(30)
);
INSERT INTO tab_ref_2 VALUES(1, 'ee');
INSERT INTO tab_ref_2 VALUES(2, 'ff');
INSERT INTO tab_ref_2 VALUES(3, 'gg');
INSERT INTO tab_ref_2 VALUES(4, 'hh');
CREATE TABLE SUMMARY_TAB
(
TOT_RECORDS NUMBER(10,0),
SUCCESS_RECORDS NUMBER(10,0),
FAILED_RECORDS NUMBER(10,0),
process_status varchar2(30)
);
CREATE TABLE TARGET_TAB
(
E_ID NUMBER(10,0),
E_NAME VARCHAR2(30),
E_LOC VARCHAR2(30)
);
Stored procedure:
create or replace procedure sp_stage_target(iv_req_id IN sys.OdciNumberList,ov_err_msg OUT varchar2) is
lv_succ_rec number(30);
lv_fail_rec number(30);
lv_count_ref number(10);
lv_count_ref2 number(10);
lv_threshold_cnt number(10);
lv_RejectedCount number(10);
lv_status varchar2(30);
begin
lv_succ_rec := 0;
lv_fail_rec := 0;
lv_threshold_cnt := 5;
/*First checking whether data is present in reference table or not.
If data is not present then process should stop*/
select count(1) into lv_count_ref from tab_ref;
select count(1) into lv_count_ref2 from tab_ref_2;
if lv_count_ref = 0 then
ov_err_msg := 'Records are not present in the reference table !!Cannot proceed';
elsif lv_count_ref2 = 0 then
ov_err_msg := 'Records are not present in the reference table !!Cannot proceed';
else
dbms_output.put_line('Data are present into reference tables');
merge into staging d
using (
select 'Fail' as validation_status, t.column_value as req_id
from table(iv_req_id) t
) s
on (d.req_id = s.req_id)
when matched then
update set
d.validation_status = s.validation_status
, d.validation_msg = case
when e_id is null then 'Id is not present'
else 'Id is longer than expected'
end
where e_id is null OR LENGTH(e_id) > 4;
lv_RejectedCount := SQL%ROWCOUNT;
end if;
--If rejected count is less than lv_threshold_cnt i.e 5
--then success records will go in target_tab and failed records will go in reject_tab
if lv_RejectedCount <= lv_threshold_cnt then
lv_status := 'Success';
dbms_output.put_line('Success');
merge into target_tab t
using (
select e_id, e_name, e_loc
from staging
where validation_status is null and req_id in (select column_value from table(iv_req_id))
) s
on (t.e_id = s.e_id)
when matched then
update set
t.e_name = s.e_name,
t.e_loc = s.e_loc
when not matched then
insert (t.e_id,t.e_name,t.e_loc)
values (s.e_id,s.e_name,s.e_loc);
lv_succ_rec := SQL%ROWCOUNT;
end if;
insert into reject_tab
select e_id, e_name, e_loc, validation_status,validation_msg
from staging
where validation_status = 'Fail' and req_id in (select column_value from table(iv_req_id));
lv_fail_rec := SQL%ROWCOUNT;
--In Summary table keeping track of all the records i.e success record, failed records
dbms_output.put_line('Inserting into Summary table');
insert into summary_tab(tot_records, success_records, failed_records, process_status)
values (lv_succ_rec + lv_fail_rec, lv_succ_rec, lv_fail_rec, lv_status);
ov_err_msg := 'Procedure completed succesfully';
end;
Calling Procedure:
set serveroutput on;
declare
err_msg;
begin
sp_main_target(sys.OdciNumberList(1,2),err_msg);
dbms_output.put_line(err_msg);
end;
Getting compilation error and also I am not not how to process for individually for each request_id and process so have highlighted the requirement in comment block.
Error :
I wanted to create a stored procedure that will handle all the below points and I have tried but not been able to get the results.
The stored procedure should accept multiple input parameters while calling a procedure and should process for every request-id given in the parameter with comma-separated.
Then stored procedure will check whether data is present or not in the ref table (tab_ref & tab_ref_2). If data is present then only the process should start otherwise it will not proceed further.
Then it will check the data in the staging table and will do validation for e_id is null or not or its length should not exceed the given limit. For every failed validation it will update the validation status and validation msg column and have to keep count of rejected columns.
After validation, if the threshold count is less then the lv_threshold_count then insertion will happen in both the tables with status as 'Success 'i.e target table and rejected table with validation_status as null and Fail respectively.
If threshold count is more than the lv_threshold_count then it will insert into the rejected table with status as 'Fail'.
Then at last it will show all the records count into the summary table.
You start an IF on line 20 of your procedure, but you don't have a corresponding END IF.
if lv_count_ref = 0 then

Number of rows UPDATED and INSERTED from the same procedure in PL/SQL

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.

How to create a table using a function/procedure in plsql using dynamic user input

How to create a table using function/procedure in plsql so that it should take table name as a dynamic variable and create that table with provided parameters ?
A) I TRIED THIS :
CREATE OR REPLACE PROCEDURE CREATE_TABLE IS
UN VARCHAR2(20) :=&TABLE_NAME;
BEGIN
CREATE TABLE UN(CUSTOMER_ID NUMBER,ORDER_DATE VARCHAR2(20),ORDER_MODE VARCHAR2(20),ORDER_STATUS NUMBER,SALES_REP_ID NUMBER,ORDER_TOTAL NUMBER,
PROMOTION_ID CHAR,ERROR_MESSAGE VARCHAR2(30),RECORD_STATUS CHAR);
end;
insert into UN(select distinct CUSTOMER_ID,TO_CHAR(TO_TIMESTAMP(ORDER_DATE),'DD-MM-YYYY'),UPPER(ORDER_MODE),UPPER(ORDER_STATUS),SALES_REP_ID,ORDER_TOTAL,PROMOTION_ID,ERROR_MESSAGE,RECORD_STATUS
FROM LIKHITH where ORDER_TOTAL >0 AND ORDER_TOTAL NOT LIKE '%.%');
IF COUNT(CUSTOMER_ID)=9 THEN
(
UPDATE LIKHITH SET (RECORD_STATUS='E' AND ERROR_MESSAGE='ERR') WHERE (ORDER_TOTAL<0 OR ORDER_TOTAL NOT LIKE '%.%');
UPDATE LIKHITH SET RECORD_STATUS='P' WHERE (ORDER_TOTAL<0 AND ORDER_TOTAL NOT LIKE '%.%');
UPDATE UN SET RECORD_STATUS='P';
)
END IF;
)
END;
/
You need to use Dynamic SQL. There were many redundant parentheses in your code which was not required.
Also this line IF COUNT(CUSTOMER_ID)=9 has no meaning. You should do a select from the table and do the comparison.
CREATE OR REPLACE PROCEDURE CREATE_TABLE
( un VARCHAR2)
IS
v_count NUMBER;
BEGIN
EXECUTE IMMEDIATE 'CREATE TABLE '||
UN ||
'
(
CUSTOMER_ID NUMBER,
ORDER_DATE VARCHAR2(20),
ORDER_MODE VARCHAR2(20),
ORDER_STATUS NUMBER,
SALES_REP_ID NUMBER,
ORDER_TOTAL NUMBER,
PROMOTION_ID CHAR,
ERROR_MESSAGE VARCHAR2(30),
RECORD_STATUS CHAR
)';
EXECUTE IMMEDIATE 'insert into '||
UN ||
'
select distinct
CUSTOMER_ID,
TO_CHAR(TO_TIMESTAMP(ORDER_DATE),''DD-MM-YYYY''),
UPPER(ORDER_MODE),
UPPER(ORDER_STATUS),
SALES_REP_ID,
ORDER_TOTAL,
PROMOTION_ID,
ERROR_MESSAGE,
RECORD_STATUS
FROM
LIKHITH
where ORDER_TOTAL >0
AND ORDER_TOTAL NOT LIKE ''%.%''' ;
EXECUTE IMMEDIATE 'SELECT COUNT(CUSTOMER_ID) FROM ' ||
UN INTO v_count;
IF v_count = 9 THEN
UPDATE
LIKHITH
SET RECORD_STATUS ='E' ,
ERROR_MESSAGE ='ERR'
WHERE ORDER_TOTAL <0
OR ORDER_TOTAL NOT LIKE '%.%';
ELSE
UPDATE
LIKHITH
SET RECORD_STATUS ='P'
WHERE ORDER_TOTAL <0
AND ORDER_TOTAL NOT LIKE '%.%';
EXECUTE IMMEDIATE 'UPDATE '||
UN ||
' SET RECORD_STATUS=''P''';
END IF;
END;
/
Don't pass table name during compilation as you were trying.Pass it during execution as follows.
EXEC CREATE_TABLE('&table_name');

Why does this get-or-create function never seem to insert?

I'm writing a function for batch-importing data into our org-chart. It seems to work fine for retrieving entries that already exists, but when an entry does not already exist, and it's supposed to insert, commit, and return the result of a re-attempt (so as to get the auto-generated ID), it always returns NULL.
I'm sure I'm doing something wrong here, but what? Help appreciated.
Note: there's a before-insert trigger that fills in DEPT_ID if it's not specified. Works fine if the insert statement is executed by hand.
CREATE TABLE DEPTS
(
"DEPT_ID" VARCHAR2(10 BYTE),
"HEADER_ID" VARCHAR2(10 BYTE),
"COMMENTS" VARCHAR2(100 BYTE),
"CATEGORY" VARCHAR2(2 BYTE)
);
CREATE OR REPLACE FUNCTION get_or_make_unit(
, in_cat VARCHAR2
, in_cmt VARCHAR2
, in_hdr VARCHAR2 DEFAULT NULL
) RETURN VARCHAR2 AS
unit_id VARCHAR2(10);
BEGIN
unit_id := NULL;
IF in_hdr IS NULL THEN
SELECT dept_id
INTO unit_id
FROM depts unit
WHERE unit.category = in_cat
AND unit.comments = in_cmt
AND unit.header_id IS NULL;
ELSE
SELECT dept_id
INTO unit_id
FROM depts unit
WHERE unit.category = in_cat
AND unit.comments = in_cmt
AND unit.header_id = in_hdr;
END IF;
IF unit_id IS NULL THEN
DBMS_OUTPUT.PUT_LINE('Inserting!');
INSERT INTO depts (
header_id
, comments
, category
) VALUES (
in_hdr
, in_cmt
, in_cat);
COMMIT;
unit_id := get_or_make_unit(in_cat, in_cmt, in_hdr);
RETURN unit_id;
ELSE
DBMS_OUTPUT.PUT_LINE('Not inserting!');
RETURN unit_id;
END IF;
END get_or_make_unit;
And the trigger:
CREATE OR REPLACE TRIGGER HRD.DEPTS_BIR
BEFORE INSERT
ON HRD.DEPTS
FOR EACH ROW
DECLARE
JML NUMBER;
BEGIN
SELECT SEQ_DEPT_ID.NEXTVAL INTO JML FROM DUAL;
:NEW.DEPT_ID:='D'||to_char(JML);
END DEPTS_BIR;
Examples
This works:
INSERT INTO depts (
header_id
, comments
, category
) VALUES (
'D532'
, 'ACCOUNTING'
, '2');
COMMIT;
SELECT get_or_make_unit('2', 'ACCOUNTING', 'D532') FROM DUAL;
=> 'D533'
This does not:
SELECT get_or_make_unit('2', 'NEW DEPT', 'D532') FROM DUAL;
=> NULL
Instead of:
INSERT INTO depts (
header_id
, comments
, category
) VALUES (
in_hdr
, in_cmt
, in_cat);
COMMIT;
unit_id := get_or_make_unit(in_cat, in_cmt, in_hdr);
use RETURNING INTO:
INSERT INTO depts (
header_id
, comments
, category
) VALUES (
in_hdr
, in_cmt
, in_cat) RETURNING dept_id INTO unit_id;
COMMIT;
I think recursive call in not the best approach, but if you are strict of using it, then please post definition of mentioned before-insert trigger.
UPDATE: You cannot call functions containing DML operations from SQL statement. Please see this answer for details. Example of correct call:
DECLARE
unit_id varchar2(32);
BEGIN
unit_id := get_or_make_unit('2', 'NEW DEPT', 'D532');
dbms_output.put_line(unit_id);
END;
UPDATE2: Also you need to catch NO_DATA_FOUND exception that is raised when you call your function with not existent combination. Below is example:
CREATE OR REPLACE FUNCTION get_or_make_unit(in_cat VARCHAR2,
in_cmt VARCHAR2,
in_hdr VARCHAR2 DEFAULT NULL)
RETURN VARCHAR2 AS
unit_id VARCHAR2(10);
BEGIN
unit_id := NULL;
IF in_hdr IS NULL THEN
SELECT dept_id
INTO unit_id
FROM depts unit
WHERE unit.category = in_cat
AND unit.comments = in_cmt
AND unit.header_id IS NULL;
ELSE
SELECT dept_id
INTO unit_id
FROM depts unit
WHERE unit.category = in_cat
AND unit.comments = in_cmt
AND unit.header_id = in_hdr;
END IF;
DBMS_OUTPUT.PUT_LINE('Not inserting!');
RETURN unit_id;
exception
when NO_DATA_FOUND then
DBMS_OUTPUT.PUT_LINE('Inserting!');
INSERT INTO depts
(header_id, comments, category)
VALUES
(in_hdr, in_cmt, in_cat)
returning dept_id into unit_id;
COMMIT;
RETURN unit_id;
END get_or_make_unit;
Your call to get_or_make_unit appears to be missing the first parameter.
INSERT INTO depts (
header_id
, comments
, category
) VALUES (
in_hdr
, in_cmt
, in_cat);
This code is not inserting anything into dept_id of depts table. Thats the reason you are getting null

can any solve what is wrong with this please?

create table account_type
(
acct_type number(3) primary key,
acct_desc Varchar2(30) not null CHECK (acct_desc IN('savings','salary','current','credit')),
acct_wd_limit number(10)
);
create sequence acct_seq;
CREATE OR REPLACE TRIGGER acct_pk
BEFORE INSERT ON account_type
FOR EACH ROW
WHEN (new.acct_type IS NULL)
BEGIN
SELECT acct_seq.NEXTVAL
INTO :new.acct_type
FROM account_type;
END;
i am getting an error on the line before insert on account_type. no idea why
I also tried doing
CREATE OR REPLACE TRIGGER acct_pk
BEFORE INSERT ON account_type
FOR EACH ROW
WHEN (new.id IS NULL)
BEGIN
SELECT acct_seq.NEXTVAL
INTO :new.id
FROM account_type;
END;
Even doing this is giving me an error
create sequence acct_pk
start with 1
increment by 1
max value 999
min value 1
no cycle;
Thanks
Since the author tagged the question with oracle10g, she can't use sequence_name.nextVal in PL/SQL. Solution:
CREATE OR REPLACE TRIGGER acct_pk
BEFORE INSERT ON account_type
FOR EACH ROW
DECLARE
v_acct_type NUMBER;
BEGIN
IF :new.acct_type IS NULL THEN
SELECT acct_seq.NEXTVAL
INTO v_acct_type
FROM dual;
:new.acct_type := v_acct_type;
END IF;
END;

Resources