Foreign key between two databases - sql

I went through the previous answers to create a pseudo-foreign key to reference tables between two databases in Netbeans 8.1. This is the code I came up with,
DELIMITER //
CREATE OR REPLACE TRIGGER conf_track_FK
AFTER INSERT OR UPDATE on S26994437.track#FIT5148B
FOR EACH ROW
BEGIN
IF EXISTS(select * from inserted I where not exists (select * from
S1234567.conference#FIT5148A A where I.conf_id=A.conf_id))
RAISE_APPLICATION_ERROR(-20001,'Violation of pseudo-foreign key.');
ROLLBACK;
END IF;
END;
/
However, I encounter the following errors:
PLS-00103: Encountered the symbol ";" when expecting one of the following:
) with and or group having intersect minus start union where
connect
PLS-00103: Encountered the symbol "ROLLBACK" when expecting one of the following:
:= . ( % ;
The symbol ":=" was substituted for "ROLLBACK" to continue.
PLS-00103: Encountered the symbol "end-of-file" when expecting one of the following:
end not pragma final instantiable order overriding static
member constructor map

Create Trigger dbo.MyTableTrigger ON dbo.MyTable, After Insert, Update
As
Begin
If NOT Exists(select PK from OtherDB.dbo.TableName where PK in (Select FK from inserted) BEGIN
-- Handle the Referential Error Here
END
END

Try the below illustrated snippet. Hope it helps. And also any TRANSACTIONS like COMMIT / ROLLBACK cant be placed INSIDE Trigger unless its an Autonomous transaction but that too not a good idea as parent transaction is hidden to the trigger transaction. So even the Parent transaction fails the Automnomous transaction will be done.
CREATE OR REPLACE TRIGGER conf_track_FK AFTER
INSERT OR
UPDATE ON S26994437.track#FIT5148B --Remove DB link as it cant be used in Trigger
FOR EACH ROW
DECLARE
lv_cnt PLS_INTEGER;
BEGIN
SELECT COUNT(1)
INTO lv_cnt
FROM inserted I
WHERE NOT EXISTS
(SELECT 1 FROM S1234567.conference#FIT5148A A WHERE I.conf_id=A.conf_id
);
IF lv_cnt > 0 THEN
RAISE_APPLICATION_ERROR(-20001,'Violation of pseudo-foreign key.');
END IF;
END;
/

Related

Oracle trigger: SELECT INTO, no data found

I have the following table that describes which chemical elements each planet is composed of using percentage.
CREATE TABLE elem_in_planet
(
id_planet INTEGER,
element_symbol CHAR(2),
percent_representation NUMBER CONSTRAINT NN_elem_in_planet NOT NULL,
CONSTRAINT PK_elem_in_planet PRIMARY KEY (id_planet, element_symbol),
CONSTRAINT FK_planet_has_elem FOREIGN KEY (id_planet) REFERENCES planet (id_planet),
CONSTRAINT FK_elem_in_planet FOREIGN KEY (element_symbol) REFERENCES chemical_element (element_symbol)
);
I'm trying to make a trigger that warns users when they add a new element to a planet and the sum of elements in that planet exceeds 100%. I came up with this.
CREATE OR REPLACE TRIGGER elem_in_planet_check
AFTER INSERT OR UPDATE ON elem_in_planet
FOR EACH ROW
DECLARE
sum_var NUMBER;
PRAGMA autonomous_transaction;
BEGIN
SELECT SUM(percent_representation)
INTO sum_var
FROM elem_in_planet
WHERE id_planet = :NEW.id_planet
GROUP BY id_planet;
EXCEPTION
WHEN NO_DATA_FOUND THEN
sum_var := 0;
IF sum_var > 100 THEN
DBMS_OUTPUT.put_line('WARNING: Blah blah.');
END IF;
END;
/
This code seems to throw the NO_DATA_FOUND exception every single time, even though I have inserted test data and when I run the SQL query alone, it works as expected.
I'm new to this and don't understand what I'm doing wrong.
Thank you for any advice.
You have NOT inserted the row into the table, 2 reasons.
The trigger runs as part of the insert statement which has not
completed. So the row does not exist.
You specified "PRAGMA autonomous_transaction" (AKA the create
untraceable bug here statement), did you previously get a mutating
table exception. So you cannot see any data inserted/updated/deleted
by the current transaction.Further if an error did occur the row would still be inserted as you did not raise an error or re-raise the existing error. I suggest you familiarize yourself with the PLSQL block structure. For now you may want to try:
You could use an after statement trigger or after statement section of compound trigger to make this test, do raise_application_error if sum > 100;
BTW as it stands your "if on sum_var > 100" runs only when an error occurs. Anything after the "EXCEPTION" and before END for that block runs only when a error occurs.
create or replace trigger elem_in_planet_check
after insert or update on elem_in_planet
declare
error_detected boolean := False;
begin
for planet in
(
select id_planet, sum_var
from (select id_planet, sum(percent_representation) sum_var
from elem_in_planet
group by id_planet
)
where sum_var > 100
)
loop
dbms_output.put_line('Planet ' || planet.id_planet || ' at '|| planet.sum_var || '% resources exceed 100%');
error_detected:= True;
end loop;
if error_detected then
Raise_application_error('-20001', 'Planet resources cannot exceed 100%');
end if;
end elem_in_planet_check;

Error(6,87): PLS-00103: Encountered the symbol "JOIN" when expecting one of the following/compound trigger

When I try to compile below compound trigger I got the error message. Please suggest what can be done to clear those error.
I tried to use normal trigger but its throwing ORA-04091 ERROR.
create or replace TRIGGER "WS5108"."AL_PROJECT_ORACLE_CODE_TRG" FOR
INSERT
ON ITIB_REQUESTS
COMPOUND TRIGGER
DECLARE
V_CODE varchar (200);
BEGIN
IF :NEW."J_PROJECT_ORACLE_CODE" IS NULL THEN
SELECT distinct (PROJECT_ORACLE_CODE) INTO V_CODE
FROM ITIB_PROJECT_ORACLE_CODE POC
JOIN ITIB_VPDOMAIN VP ON (POC.VP_DOMAIN = VP.VP_DOMAIN)
WHERE POC.VP_DOMAIN = (SELECT VP_DOMAIN
FROM ITIB_VPDOMAIN
WHERE ID = :NEW."VP_DOMAIN")
AND CAPEX_CATEGORY = :NEW."C_CAPEX_CATEGORY"
AND :NEW."C_TOTAL_EURO" <= 250 ;
END IF;
:NEW.J_PROJECT_ORACLE_CODE := V_CODE;
EXCEPTION
when no_data_found then
V_CODE := null ;
END AL_PROJECT_ORACLE_CODE_TRG;
I am trying to add value for one column in table after new line inserted in that table if that column is empty. This column value I am taking from that select condition and new values which are inserted in table.
The error in your question title is the second of two; running the reformatted code in your question I see:
LINE/COL ERROR
--------- -------------------------------------------------------------
2/1 PLS-00103: Encountered the symbol "DECLARE" when expecting one of the following: function pragma procedure subtype type <an identifier> <a double-quoted delimited-identifier> current cursor delete exists prior before after instead
8/9 PLS-00103: Encountered the symbol "JOIN" when expecting one of the following: , ; for group having intersect minus order start union where connect
(with different line number due to reformatting). In general you should fix the first error reported first, as subsequent errors are often unhelpful and caused by side-effects of the earlier ones. That is the case here. If you fix the compound trigger syntax to address the error reported against DECLARE then the JOIN is no longer a problem either:
create or replace TRIGGER "WS5108"."AL_PROJECT_ORACLE_CODE_TRG"
FOR INSERT
ON ITIB_REQUESTS
COMPOUND TRIGGER
BEFORE EACH ROW IS
V_CODE varchar (200);
BEGIN
IF :NEW."J_PROJECT_ORACLE_CODE" IS NULL THEN
SELECT distinct PROJECT_ORACLE_CODE INTO V_CODE
...
END IF;
:NEW.J_PROJECT_ORACLE_CODE := V_CODE;
EXCEPTION
when no_data_found then
V_CODE := null ;
END BEFORE EACH ROW;
END AL_PROJECT_ORACLE_CODE_TRG;
/
Basically, the main trigger body code you had needed to be in a BEFORE EACH ROW block, since it is a compound trigger.
Incidentally, you probably want V_CODE to be declared as varchar2 rather than varchar, or as ITIB_PROJECT_ORACLE_CODE.PROJECT_ORACLE_CODE%TYPE. If you need that variable at all; the assignment back to the :NEW field probably wants to be inside the IF block (as if that is not initially null, you set it to v_code, which is then null!), but you can also select straight into the :NEW variable. So I think you really want something more like:
create or replace TRIGGER WS5108.AL_PROJECT_ORACLE_CODE_TRG
FOR INSERT
ON ITIB_REQUESTS
COMPOUND TRIGGER
BEFORE EACH ROW IS
BEGIN
IF :NEW.J_PROJECT_ORACLE_CODE IS NULL THEN
SELECT PROJECT_ORACLE_CODE
INTO :NEW.J_PROJECT_ORACLE_CODE
FROM ITIB_PROJECT_ORACLE_CODE POC
JOIN ITIB_VPDOMAIN VP ON (POC.VP_DOMAIN = VP.VP_DOMAIN)
WHERE POC.VP_DOMAIN = (SELECT VP_DOMAIN
FROM ITIB_VPDOMAIN
WHERE ID = :NEW."VP_DOMAIN")
AND CAPEX_CATEGORY = :NEW.C_CAPEX_CATEGORY
AND :NEW.C_TOTAL_EURO <= 250 ;
END IF;
EXCEPTION
when no_data_found then
null ; -- do nothing; :NEW.J_PROJECT_ORACLE_CODE is already null
END BEFORE EACH ROW;
END AL_PROJECT_ORACLE_CODE_TRG;
/
I don't really see why this needs to be a compound trigger though, you aren't accessing the table the trigger is against (as you were in your previous question) and you only have a single scenario - only before insert; so a simple trigger would work here:
create or replace WS5108.TRIGGER AL_PROJECT_ORACLE_CODE_TRG
BEFORE INSERT
ON ITIB_REQUESTS
FOR EACH ROW
BEGIN
IF :NEW.J_PROJECT_ORACLE_CODE IS NULL THEN
SELECT PROJECT_ORACLE_CODE
INTO :NEW.J_PROJECT_ORACLE_CODE
FROM ITIB_PROJECT_ORACLE_CODE POC
JOIN ITIB_VPDOMAIN VP ON (POC.VP_DOMAIN = VP.VP_DOMAIN)
WHERE POC.VP_DOMAIN = (SELECT VP_DOMAIN
FROM ITIB_VPDOMAIN
WHERE ID = :NEW.VP_DOMAIN)
AND CAPEX_CATEGORY = :NEW.C_CAPEX_CATEGORY
AND :NEW.C_TOTAL_EURO <= 250 ;
END IF;
EXCEPTION
when no_data_found then
null ; -- do nothing; :NEW.J_PROJECT_ORACLE_CODE is already null
END AL_PROJECT_ORACLE_CODE_TRG;
/
And even the query within that looks confused; you don't need the subquery:
...
SELECT PROJECT_ORACLE_CODE
INTO :NEW.J_PROJECT_ORACLE_CODE
FROM ITIB_PROJECT_ORACLE_CODE POC
JOIN ITIB_VPDOMAIN VP ON (POC.VP_DOMAIN = VP.VP_DOMAIN)
WHERE VP.ID = :NEW.VP_DOMAIN
AND CAPEX_CATEGORY = :NEW.C_CAPEX_CATEGORY
AND :NEW.C_TOTAL_EURO <= 250 ;
...

Issue with trigger for archiving

Trying to make a trigger that puts data into an archive table when a column called COMPLETION_STATUS goes from incomplete to complete, the dbms is a placeholder for the insert but I'm getting the following errors in the if statement
Error(6,1): PLS-00103: Encountered the symbol enter code here"SELECT" when expecting one of the following: begin function pragma procedure subtype type current cursor delete exists prior The symbol "begin" was substituted for "SELECT" to continue.
Error(9,1): PLS-00103: Encountered the symbol "IF" when expecting one of the following: * & - + ; / at for mod remainder rem and or group having intersect minus order start union where connect || multiset The symbol ";" was substituted for "IF" to continue.
Error(13,4): PLS-00103: Encountered the symbol "end-of-file" when expecting one of the following: ( begin case declare end exception exit for goto if loop mod null pragma raise return select update while with << continue close current delete fetch lock insert open rollback savepoint set sql execute commit forall merge pipe purge
Code:
create or replace TRIGGER ARCHIVING_TRIG
BEFORE UPDATE OF COMPLETION_STATUS ON PROJECT_DATA
BEGIN
DECLARE COMPLETION_STATUS1 VARCHAR2(9);
SELECT COMPLETION_STATUS into COMPLETION_STATUS1
FROM PROJECT_DATA WHERE COMPLETION_STATUS = 'complete'
IF COMPLETION_STATUS1 = 'complete'
THEN
DBMS.output('123');
END IF;
END;
The DECLARE block should be before the BEGIN block.
The SELECT ... statement needs to be terminated with a semicolon (;).
It's dbms_output.put_line() not dbms.output();
You're trying to assign the result of a query that potentially can return more than one row to a scalar variable.
The rows selected from project_data have no relation to the one(s) that triggered the trigger.
I suggest you use something like:
CREATE TRIGGER archiving_trig
AFTER UPDATE
ON project_data
FOR EACH ROW
WHEN (old.completion_status <> 'complete'
AND new.completion_status = 'complete')
BEGIN
dbms_output.put_line('Trigger fired for ID ' || :new.id);
END;
db<>fiddle
I think maybe AFTER is the better time, because you want to archive the row after the status was successfully changed.
Because of the WHEN the trigger will only fire if completion_status has been changed from something other than 'complete' to 'complete'. But you maybe also need to have a method of removing entries from the archive when the status changes from 'complete' to something else. That isn't covered here.
Declaring it as FOR EACH ROW let's you access the values of the updated row via :new. That way you don't need a query to select that nor a variable to select into.
I guess you need this:
create table PROJECT_DATA_NEW as select * from PROJECT_DATA where 1=2;
CREATE OR REPLACE TRIGGER ARCHIVING_TRIG
AFTER UPDATE
ON PROJECT_DATA
FOR EACH ROW
DECLARE
status number;
BEGIN
status:=0;
select 1 into status from PROJECT_DATA where
:new.COMPLETION_STATUS='complete' and
:old.COMPLETION_STATUS='incomplete'
if (status=1) then
insert into PROJECT_DATA_NEW values(:old.column1,
:old.column2,
:old.column3,
:old.column4,
:old.column5,....etc);
end if;
END;
/

ORA-24344: success with compilation error - Trigger APEX

I've been working around this trigger and when I run the script it tells me the previous error message. I can't seem to figure out why it won't compile correctly, every pl/sql trigger tutorial seems to have the structure my trigger has. Code is the following:
create
or replace trigger new_artist before insert
on
Artist referencing new as nvartist declare counter number;
begin select
count( * ) into
counter
from
Performer
where
Stage_name = nvartist.Stage_name;
if counter > 0 then signal sqlstate '45000';
else insert
into
Artist
values(
nvartist.Stage_name,
nvartist.Name
);
insert
into
Performer
values(nvartist.Stage_name);
end if;
end;
It checks if the new artist already exists in its supertype (Performer), if it does exist it gives an error if it doesn't it inserts both into artist(Stage_name varchar2, Name varchar2) and Performer(Stage_name). Another subtype of Performer (and sibling to Artist) is Band(Stage_name), which in turn has a relationship with Artist. Why does the compiler yell at me for this trigger?
Thanks in advance
You may want to try this variant (I slightly modified names of your tables).
Creating tables with sample data:
CREATE table test_artist(
stage_name varchar2(100)
, name varchar2(100)
);
create table test_performer(
stage_name varchar2(100)
);
/*inserting test performer on which trigger will rise an error*/
insert into test_performer
select 'performer_1' from dual;
Creating trigger:
create or replace trigger new_artist
before insert
on TEST_ARTIST
referencing new as nvartist
for each row
declare
counter number;
begin
select count(*)
into counter
from test_performer
where Stage_name = :nvartist.Stage_name;
if counter > 0 then
--signal sqlstate '45000' ;
raise_application_error( -20001, 'No insertion with existing Performer');
else
/*you cant update the same table, in other case you'll get
ora-04091 table mutating error.
But nevertheless this values will be inserted by sql triggered this trigger.*/
--insert into test_artist values(:nvartist.Stage_name, :nvartist.Name);
insert into test_performer values(:nvartist.Stage_name);
end if;
end new_artist;
After that this insert will work, cause the is no 'performer_2' in 'test_performer' table:
insert into test_artist
select 'performer_2', 'name_2' from dual;
And this will fail:
insert into test_artist
select 'performer_1', 'name_1' from dual;

Trigger in Oracle giving error

This is my sql code:
CREATE OR REPLACE TRIGGER PLACE_NO_TRIGGER
BEFORE INSERT or UPDATE ON UHA_LEASE
FOR EACH ROW BEGIN
INSERT INTO UHA_TEMPVAL SELECT PLACE_NO FROM UHA_RESHALL;
INSERT INTO UHA_TEMPVAL SELECT PLACE_NO FROM UHA_GENERAL;
IF(:NEW.PLACE_NO NOT IN UHA_TEMPVAL.TEMP_VALUE AND :NEW.PLACE_NO IS NOT NULL) THEN
RAISE_APPLICATION_ERROR(-10001, 'PLACE_NO MUST BE IN UHA_RESHALL OR IN UHA_GENERAL OR NULL');
END IF;
DELETE FROM UHA_TEMPVAL;
END;
This is giving these errors:
Error(4,27): PLS-00103: Encountered the symbol "UHA_TEMPVAL" when expecting one of the following: ( The symbol "(" was substituted for "UHA_TEMPVAL" to continue.
Error(4,69): PLS-00103: Encountered the symbol "THEN" when expecting one of the following: ) , and or as The symbol ")" was substituted for "THEN" to continue.
Can someone help me understand why these errors are occurring? It's especially odd because row 4 ends at column 60.
Thanks!
It looks like you're trying to enforce referential integrity from a child table to either of two parent tables. You don't really need to insert and delete from a temporary table; you can count how many rows match in either table:
CREATE OR REPLACE TRIGGER PLACE_NO_TRIGGER
BEFORE INSERT or UPDATE ON UHA_LEASE
FOR EACH ROW
DECLARE
CNT NUMBER;
BEGIN
SELECT COUNT(*)
INTO CNT
FROM (
SELECT PLACE_NO FROM UHA_RESHALL
WHERE PLACE_NO = :NEW.PLACE_NO
UNION ALL
SELECT PLACE_NO FROM UHA_GENERAL
WHERE PLACE_NO = :NEW.PLACE_NO
);
IF :NEW.PLACE_NO IS NOT NULL AND CNT = 0 THEN
RAISE_APPLICATION_ERROR(-10001,
'PLACE_NO MUST BE IN UHA_RESHALL OR IN UHA_GENERAL OR NULL');
END IF;
END;
/
You could choose to only run the query if the :NEW.PLACE_NO is not null but it probably won't make much difference if the columns are indexed.