Insert procedure - check if foreign key exists in primary table - plsql

I have the following procedure (insert_mapping):
create or replace procedure p_insert_mapping
(header_id in number,
position in number,
xml_mapping in varchar2,
id out number,
result_code out number
)
is
l_id number;
begin
-- check for errors
if header_id not in (select log_push_readouts_headers.id from log_push_readouts_headers) then
result_code := 9302;
raise_application_error(-RESULT_CODE, 'Foreign key constraint violated for headers');
end if;
-- if there are no errors, do insert
if result_code is null then
-- fetch sequence number
id := mapping_seq.nextval;
insert into log_push_readouts_mappings
(id, position, xml_mapping)
values
(id, position, xml_mapping);
end if;
commit;
end;
In the following line:
if header_id not in (select log_push_readouts_headers.id from log_push_readouts_headers) then
I need to check if foreign key exists in main table.
How to do that?
Could someone give me an example on how to check if foreign key is in a table with primary key?
I am getting the following error: Compilation errors for PROCEDURE
AMM_MDM.P_INSERT_MAPPING
Error: PLS-00405: subquery not allowed in this context
Line: 12
Text: if header_id not in (select log_push_readouts_headers.id from log_push_readouts_headers) then
Error: PL/SQL: Statement ignored
Line: 12
Text: if header_id not in (select log_push_readouts_headers.id from log_push_readouts_headers) then

regarding the compilation error, please refer
Using a subquery within PLSQL conditional logic; error PLS-00405
and for checking foreign key constraint on a column of a table, we can query from all_constrainsts, where CONSTRAINT_TYPE can be checked
doc ref: https://docs.oracle.com/cd/B14117_01/server.101/b10755/statviews_1037.htm#i1576022

What you can do is fetch the matching count of sub-query into a number type variable and then use that in if-else statement to compute the flow of your code
select count(1) into <some variable> from log_push_readouts_headers a where log_push_readouts_headers.id = header_id
Now check this count against your desired value in if-else statement.

Related

Procedure run details for a day

Please let me know if you can provide the detail of “number of times” a procedure has been called for that particular day, for the all the valid procedures.
You can use your own logging technic. For example first you can create a table under the same schema of your desired procedure. Then after begin statement end before end statement in that invoked procedure, you can insert logs to newly created log table of yours.
CREATE TABLE SCHEMA.LOGTABLE
(
DATECOLUMN DATE DEFAULT SYSTIMESTAMP,
PROCNAME VARCHAR2 (200 CHAR),
TABLENAME VARCHAR2 (200 CHAR),
MESSAGE VARCHAR2 (1000 CHAR),
LOGSEQUENCE NUMBER
);
CREATE OR REPLACE PROCEDURE SCHEMA.PROCNAME IS
BEGIN
INSERT INTO SCHAME.LOGTABLE(DATECOLUMN,
PROCNAME,
TABLENAME,
MESSAGE,
LOGSEQUENCE)
VALUES (SYSTIMESTAMP,
'SCHEMA.PROCNAME',
'SCHEMA.TABLENAME',
'Proc STARTED',
NULL,
SCHEMA.SEQ_SISTEM_LOG.NEXTVAL);
COMMIT;
.....
INSERT INTO SCHAME.LOGTABLE(DATECOLUMN,
PROCNAME,
TABLENAME,
MESSAGE,
LOGSEQUENCE)
VALUES (SYSTIMESTAMP,
'SCHEMA.PROCNAME',
'SCHEMA.TABLENAME',
'Proc ENDED',
NULL,
SCHEMA.SEQ_SISTEM_LOG.NEXTVAL);
COMMIT;
END;

Trigger for checking values for inserting

I have two tables that here are involved from two different schemas.
Schema services and table task -- column ID
Schme mona_internal and table officius_unos -- column task
I need trigger when inserting in column task table officius_unos to check does exist inserting value in column id from table task. If exist, to continue inserting, it doesn't exist to raise the error.
Here is the trigger:
CREATE OR REPLACE TRIGGER mona_internal.PROBA_PROBA
BEFORE INSERT ON OFFICIUS_UNOS
FOR EACH ROW
DECLARE
task_provera number(10);
BEGIN
select id into task_provera from servis.task
where id=:new.task;
if (task_provera is null)
then raise_application_error(-20101, 'No task');
else insert into mona_internal.OFFICIUS_UNOS (task) values (:new.task);
end if;
END;
The trigger is compiled, but the problem appears when trying to insert a new value in column task table officius_unos,
it returns me this message
insert into officius_unos (task) values (291504);
Error report -
ORA-00036: maximum number of recursive SQL levels (50) exceeded
ORA-00036: maximum number of recursive SQL levels (50) exceeded
ORA-06512: at "MONA_INTERNAL.PROBA_PROBA", line 5
ORA-04088: error during execution of trigger 'MONA_INTERNAL.PROBA_PROBA'
ORA-06512: at "MONA_INTERNAL.PROBA_PROBA", line 10
And value 291504 exist in table task in column id.
P.S. Also try to solve this problem with check constraint, but there are forbidden subqueries. The solution that I used to overcome my problem is here
Oracle: Using subquery in a trigger
You don't need to insert in an insert trigger.
If the trigger is successful Oracle will continue with the INSERT on its own.
So the immediate solution is to remove the INSERT from the trigger:
CREATE OR REPLACE TRIGGER mona_internal.PROBA_PROBA
BEFORE INSERT ON OFFICIUS_UNOS
FOR EACH ROW
DECLARE
task_provera number(10);
BEGIN
select id
into task_provera
from servis.task
where id=:new.task;
if (task_provera is null) then
raise_application_error(-20101, 'No task');
end if;
// nothing do do here
END;
However the above still isn't correct. If the id doesn't exist in servis.tak the SELECT will throw a NO_DATA_FOUND exception.
One solution to that is to use an aggregate function that will always return one row. If no rows match the WHERE criteria, a NULL value is returned:
CREATE OR REPLACE TRIGGER mona_internal.PROBA_PROBA
BEFORE INSERT ON OFFICIUS_UNOS
FOR EACH ROW
DECLARE
task_provera number(10);
BEGIN
select max(id)
into task_provera
from servis.task
where id=:new.task;
if (task_provera is null) then
raise_application_error(-20101, 'No task');
end if;
// nothing do do here
END;
Or you could explicitely catch the exception:
CREATE OR REPLACE TRIGGER mona_internal.PROBA_PROBA
BEFORE INSERT ON OFFICIUS_UNOS
FOR EACH ROW
DECLARE
task_provera number(10);
BEGIN
select max(id)
into task_provera
from servis.task
where id=:new.task;
if (task_provera is null) then
raise_application_error(-20101, 'No task');
end if;
EXCEPTION
WHEN NOT_DATA_FOUND THEN
raise_application_error(-20101, 'No task');
END;
But the correct approach is to use a foreign key constraint for something like that.
alter table mona_internal.PROBA_PROBA
add constraint fk_proba_task
foreign key (task)
references servis.task (id);
Then you don't need a trigger at all.
This requires that the user mona_internal is not only granted the SELECT privilege on servis.task, but also the references privilege:
To do that, run the following as the SERVIS user:
grant references on task to mona_internal;

How to use variable of declare at the where of select?

How to use variable of declare at the where of select ?
CREATE TABLE student (
id smallint PRIMARY KEY,
first_name VARCHAR(80) NOT NULL,
last_name VARCHAR(80) NOT NULL);
insert into student VALUES(10,'AA','A01');
insert into student VALUES(30,'BB','B01');
---MSSQL OK
declare #v_first varchar(10);
set #v_first='AA';
select * from student where first_name=#v_first;
---ORACLE --ERROR
declare v_first varchar2(10);
BEGIN
v_first :='AA';
select * from student where first_name= v_first;
END;
---=> MESSAGE
ORA-06550: line 4 , column 3 :
PLS-00428: 在此 SELECT 敘述句中預期會出現一個 INTO 子句
06550. 00000 - "line %s, column %s:\n%s"
*Cause: Usually a PL/SQL compilation error.
*Action:
The example you gave for MSSQL is a different table, but assuming DEPT_ID from USER_LIST is a NUMBER too... SQL will automatically convert the string to compare...
You need to cast the id to a number. Is there a reason you are declaring a varchar variable right before comparing it against a number?
declare v_id varchar2(2);
BEGIN
v_id :='30';
select * from student where id= TO_NUMBER(v_id);
END;
or change the declare and the assignment...
declare v_id NUMBER;
BEGIN
v_id :=30;
select * from student where id= v_id;
END;
basically, what the link I added in the comments below said.. from user tvm.. "You cannot use SELECT without INTO clause in PL/SQL. The output of the SELECT must be stored somewhere. Eg. table or variable or perhaps a record. See example below how to store result of the SELECT statement into record."
DECLARE
v_id number;
v_result Student%ROWTYPE;
BEGIN
v_id := 30;
SELECT * INTO v_result
FROM Student
WHERE id = v_id;
END;
Then you need to do something with v_result... other option shown is to call straight SQL - all depends on how you call and consume the data..

PlSQL trigger error ORA-0000 ORA-06512:

create or replace
TRIGGER "SUP" AFTER INSERT ON "EMP_REPORT" REFERENCING OLD AS "OLD" NEW AS "NEW" FOR EACH ROW
DECLARE
miclaim_supervisor_count number;
employee_company_code VARCHAR2(10);
employee_businessunit number;
projMgrs NUMBER;
supId NUMBER;
cursor projMgrsCursor is select b.BU_MEMBER_ID
from BU_MEMBER b, EMP_SUB_DIV s
where s.EMP_NO = :NEW.EMP_NO
and s.SUB_DIVISION_CODE = '01' and s.DIV_CODE = '2'
and b.BU_ID IN (select BU_ID from BU_MEMBER where BU_MEMBER_ID = :NEW.EMP_NO);
BEGIN
delete from MICL_SUPERVISORS where EMP_NO = :NEW.EMP_NO and IS_OVVERRIDDEN = 0;
select count(*) into miclaim_supervisor_count from MICL_SUPERVISORS where EMP_NO = :NEW.EMP_NO and IS_OVVERRIDDEN = 1;
select COMPANY_CODE into employee_company_code from EMPLOYEE_MASTER where EMP_NO = :NEW.EMP_NO;
projMgrs := 0;
if (employee_company_code ='F')then
OPEN projMgrsCursor;
LOOP
select micl_sup_id_seq.nextval into supId from dual;
FETCH projMgrsCursor INTO projMgrs;
EXIT WHEN projMgrsCursor%NOTFOUND;
insert into SUP VALUES ((supId), (SELECT SYSDATE FROM DUAL), :NEW.ENTRYADDEDBY_EMP_NO, 3000, 0,projMgrs, NULL,:NEW.EMP_NO);
END LOOP;
CLOSE projMgrsCursor;
else
if(miclaim_supervisor_count IS NULL or miclaim_supervisor_count<1) then
insert into SUP VALUES ((:NEW.ID), (SELECT SYSDATE FROM DUAL), :NEW.ENTRYADDEDBY_EMP_NO, 3000, 0, :NEW.SUP_EMP_NO, NULL,:NEW.EMP_NO);
end if;
end if;
END;
I created this trigger a week go but no compilation errors
But when I enter a record to EMP_REPORT it pop up an error msg saying
*INSERT INTO"EMP_REPORT" (ID, ADDEDDATE, ENTRYADDEDBY_EMP_NO, SUP_EMP_NO, EMP_NO) VALUES ('71', TO_TIMESTAMP('19-MAR-13 09.55.57.983000000 AM', 'DD-MON-RR HH.MI.SS.FF AM'), '81', '841', '5295')
ORA-00001: unique constraint (SYS_C0023329) violated
ORA-06512: at line 1
One error saving changes to table "EMP_REPORT":
Row 51: ORA-00001: unique constraint (SYS_C0023329) violated
ORA-06512: at line 1*
I cant figure out where I went wrong. pls help me :(
pls note that I cant remove constraint and its a primary key
Are you sure that the error occurs in the trigger? It looks to me like the INSERT which might be failing is the one into EMP_REPORT - possibly because there's already a row in EMP_REPORT with ID = '71'. It would be helpful if you could confirm what table SYS_C0023329 is on.
However, assuming that the message is being generated by the trigger - I only see two INSERTs in your trigger, both inserting into the SUP table, so the problem must be coming from one of these INSERTs. Either
There's already a row in SUP which has a key value (you don't show the names of the columns in your INSERT statements against SUP so I don't know what the field's name might be) greater than the current value of MICL_SUP_ID_SEQ.NEXTVAL, or
There's a row in SUP with a key value equal to the new EMP_REPORT.ID field you've supplied in the second INSERT.
This second issue, supplying a key value from one table (EMP_REPORT) as the key value for a second table (SUP) seems a bit suspicious to me, and I'd suggest looking there first. I'd think you'd want to get a value from MICL_SUP_ID_SEQ to use in the SUP table.
To verify that the issue is caused by an insert into SUP, you might try executing the following:
SELECT *
FROM ALL_CONSTRAINTS c
WHERE c.CONSTRAINT_NAME = 'SYS_C0023329';
This will tell you what table the constraint is on.
Share and enjoy.

ROWTYPE definition in pl/sql

I have written a PL/SQL Procedure which compares data between two tables and print the difference if any, but the twist is the table names to the procedure is dynamic. Here is the procedure
create or replace PROCEDURE compareTables(
tabA IN VARCHAR2, tabB IN VARCHAR2) AS
cur_tab_name USER_TABLES%ROWTYPE;
lv_sql varchar2(4000);
lv_sql2 varchar2(4000);
BEGIN
--SELECT TABLE_NAME INTO cur_tab_name FROM USER_TABLES WHERE TABLE_NAME = tabA;
lv_sql2 := 'SELECT TABLE_NAME FROM USER_TABLES WHERE TABLE_NAME = :b_tabA';
EXECUTE IMMEDIATE lv_sql2 INTO cur_tab_name USING tabA;
<<child>>
DECLARE
TYPE cursor_ref IS REF CURSOR;
cur_comp_result cursor_ref;
rec_comp_result cur_tab_name.TABLE_NAME%rowtype;
BEGIN
lv_sql := 'SELECT * FROM '||tabA||' MINUS SELECT * FROM '||tabB;
OPEN cur_comp_result FOR lv_sql;
LOOP
FETCH cur_comp_result INTO rec_comp_result;
EXIT WHEN cur_comp_result%NOTFOUND;
DBMS_OUTPUT.PUT_LINE(rec_comp_result.empid || '' || rec_comp_result.name);
END LOOP;
CLOSE cur_comp_result;
Exception
When others then
dbms_output.put_line('The Problem is '||sqlerrm);
END;
END compareTables;
Now the problem is when I compile this procedure I am getting the following error
Error at line 14: PLS-00310: with %ROWTYPE attribute, 'CUR_TAB_NAME.TABLE_NAME' must name a table, cursor or cursor-variable
line 14:rec_comp_result cur_tab_name.TABLE_NAME%rowtype;
how will I solve it?
*NB: I don't have oracle installed in my system. I am using Oracle Apex Online tool which uses
Oracle Database 11g Enterprise Edition Release 11.2.0.3.0 and
PL/SQL Release 11.2.0.3.0
As a test, go to the last line, and after the semicolon, hit enter.
I know that Pro*C in particular will gag without a line terminator at the end of the file.
You may be encounting that issue.
Outside the scope of your question consider
SELECT columns
FROM TABLE1
MINUS
SELECT columns
FROM TABLE2
and
SELECT columns
FROM TABLE2
MINUS
SELECT columns
FROM TABLE1
Use: cur_tab_name.table_name. The variable CUR_TAB_NAMEis of type USER_TABLE%ROWTYPE, thus it has several fields.

Resources