SQL trigger not compiling - sql

iam using Oracle sqldeveloper 3.2.20 and oracleXE112 as my DB. Iam trying to create a trigger, but keep getting this error
Error(2,13): PLS-00357: Table,View Or Sequence reference 'SPRAVCA.REGION_ID_REGION' not allowed in this context
I have table SPRAVCA with foreign key REGION_ID_REGION that is linked to primary key ID_REGION in table REGION. I have attribute pocet_sp that represents number of SPRAVCA entities that are assigned to specific REGION entity.
My script lookes like this
CREATE OR REPLACE TRIGGER calc_poc_sp
AFTER INSERT OR UPDATE
ON SPRAVCA
FOR EACH ROW
BEGIN
IF (SPRAVCA.REGION_ID_REGION) = (REGION.ID_REGION) THEN
NEW.POCET_SP := OLD.POCET_SP + 1;
END IF;
END calc_poc_sp;
I want to create a trigger, that will automaticly increment pocet_sp in REGION entity if any SPRAVCA with matching foregin key will be created. Thanks a lot in advance !

In oracle:
IF (SPRAVCA.REGION_ID_REGION) = (REGION.ID_REGION) THEN
doesnt work. This is not like java or any other programming language. here the sequence object is an object which has 2 methods that can access the value. CURRVAL and NEXT VAL.
NEXTVAL will increment your current value and retrieve the incremented value. but CURRVAL will retireve value that is currently.
Unfortunately. i have experienced that the CURRVAL generally doesnt work untill the NEXTVAL hasnt been called on the sequence in the same Connection session.
Please specify use case as in why u need to check the current value of the Sequence.
Still a corrected version will be:
CREATE OR REPLACE TRIGGER calc_poc_sp
AFTER INSERT OR UPDATE
ON SPRAVCA
FOR EACH ROW
DECLARE
VAL_CHECK REGION.ID_REGION%TYPE;
CURSOR REGION IS SELECT * FROM REGION;
BEGIN
--
-- SELECT SPRAVCA.REGION_ID_REGION.CURRVAL INTO VAL_CHECK FROM DUAL;
SELECT REGION_ID_REGION INTO VAL_CHECK FROM SPRAVCA <ADD YOUR WHERE CLAUSE SO THAT ONLY 1 ROW IS RETURNED>;
--I have added a loop statement so that u can iterate through all the values of the region table. If you dont want to loop then add a where clause where you see the REGION cursor being defined.
FOR 1 IN REGION LOOP
IF (VAL_CHECK) = (REGION.ID_REGION) THEN
--
NEW.POCET_SP := OLD.POCET_SP + 1;
--
END IF;
END LOOP;
END calc_poc_sp;
Please see the syntax as i do not have a DB access as of now.
Also a question:
Where have you defined what is REGION in your trigger. This will be an error as you have not defined it.

Related

How to successfully reference another table before insert with a trigger

I'm trying to create a trigger to validate if a new entry in the table registraties (registrations) contains a valid MNR (employee number) but I'm getting stuck on the part where I'm referencing the table medewerkers (employees).
Could someone help me out?
CREATE OR REPLACE TRIGGER t_MNRcontrole
BEFORE INSERT OR UPDATE
ON registraties
DECLARE
MNR_medewerkers number (SELECT MNR FROM MEDEWERKERS);
FOR EACH ROW
BEGIN
IF :new.MNR <> MNR_medewerkers
THEN raise_application_error(-20111, 'Medewerker niet herkend!');
END IF;
END;
Error message received is
ORA-24344: success with compilation error
The PL/SQL assignment operator is :=, or select x into y from z to populate from a SQL query.
FOR EACH ROW is part of the trigger spec, not the PL/SQL code.
If :new.mnr is not present in the parent table, you will get a no_data_found exception, not a mismatched variable.
It's good practice for error messages to include details of what failed.
In programming, we use indentation to indicate code structure.
A fixed version would be something like:
create or replace trigger trg_mnrcontrole
before insert or update on registraties
for each row
declare
mnr_medewerkers medewerkers.mnr%type;
begin
select mw.mnr into mnr_medewerkers
from medewerkers mw
where mw.mnr = :new.mnr;
exception
when no_data_found then
raise_application_error(-20111, 'Medewerker '||:new.mnr||' niet herkend!');
end;
However, we can implement this kind of check better using a foreign key constraint, for example:
alter table registraties add constraint registraties_mw_fk
foreign key (mnr) references medewerkers.mnr;
MNR_medewerkers number (SELECT MNR FROM MEDEWERKERS);
will always fail because its not a NUMBER, unless your table happens to only have one single entry and even then I am not sure PLSQL will allow it to pass.
The more standard case for this would be to first declare the number, then in the codeblock you do a SELECT INTO along with a WHERE clause where you make sure to only pick one specific row from the table. Then you can compare that number with the new one.
If however you are not trying to compare to one specific row, but are instead checking if the entry exists in that table.
BEGIN
SELECT 1
INTO m_variable
FROM table
WHERE MNR = :new.MNR;
EXCEPTION
WHEN TOO_MANY_ROWS THEN
m_variable = 1;
WHEN OTHERS THEN
m_variable = 0;
END;
Declare the m_variable beforehand, and then check if its 0 then report the error.
The too_many_rows is in case there is more than one row in the table with this MNR, and the OTHERS is there for the NO_DATA_FOUND, but I use OTHERS to handle everything else that could happen but probably wont.
Btw this is a code block to be included within the main code block, so between your BEGIN and IF, then just change the IF to check if the variable is 0.

how to define a trigger that gives id automatically when insert a new person?

as I described on the title, I want to write a trigger that defines to add a new staff by all giving attributes except ID, I want to trigger generate and insert it automatically. How can I do that?
I've written a code like below in PL/SQL, but it's including the sequence and I couldn't find how can I get the current max ID of my staff with using the sequence, so could you please help me, with or without using the sequence?
CREATE SEQUENCE BEFORE_INSERTING START WITH 1000 INCREMENT BY 1;
CREATE OR REPLACE TRIGGER NEW_ID_BEFORE_INSERTING
BEFORE INSERT ON STAFF
FOR EACH ROW
BEGIN
:NEW.STAFF_ID := BEFORE_INSERTING.nextval;
END;
/
By the way, this code works fine but as you see it's starting from 1000.
Perhaps you can use something like the following to find the maximum value for STAFF_ID and then redefine the sequence based on that value:
DECLARE
nMax_staff_id NUMBER;
BEGIN
SELECT MAX(STAFF_ID)
INTO nMax_staff_id
FROM STAFF;
EXECUTE IMMEDIATE 'DROP SEQUENCE BEFORE_INSERTING';
EXECUTE IMMEDIATE 'CREATE SEQUENCE BEFORE_INSERTING START WITH ' ||
nMax_staff_id + 1 || ' INCREMENT BY 1';
END;
You only need to run the above once, just to get the sequence reset. After that the trigger will use the sequence to obtain each STAFF_ID value. Note that there are other ways to redefine a sequence's value, but here we'll do The Simplest Thing That Could Possibly Work, which is to drop the sequence and then recreate it with the new starting value.
Best of luck.
in order to find the max STAFF_ID you need the below select:
select max(STAFF_ID) from STAFF;
Once you have the highest STAFF_ID you can re-create the sequence as desired.
In any case you can increment a sequence like so:
ALTER SEQUENCE seq_name INCREMENT BY 1;
I hope that helps!
Please don't hesitate to leave a comment for any further clarifications.
Ted.
Using the sequence guarantees uniqueness of STAFF_ID but does not guarantee no gaps in assigning STAFF_ID. You might end up with STAFF_ID like 100, 101, 103, 106..
First, get the max(STAFF_ID) while the system is not running. Something like
select max(staff_id) from staff;
Then, create the sequence to start from the max staff_id. Something like
create sequence staff_sequence start with <max_id> + 1 increment by 1 nocache;
"NOCACHE" minimizes the chance of having gaps in the staff_id assigned
After, use the trigger that you created to get the nextval from the seuqnece
Note the following:
- Once a sequence is invoked for nextval, that number dispatched cannot be returned to the sequnece
- Any cached sequence values will be lost if oracle database was shutdown
If your requirement is not to have gaps between staff_ids, then sequence might not be used.

update increment of serial field from several records in postgres

I want to change the increment value of all my columns that use sequencial IDs. How can I do it?
I tried the following 2 approaches, both failed:
UPDATE information_schema.sequences SET increment=1;
This doesn't work because information_schema.sequences it's a view.
I also tried to use ALTER SEQUENCE, adding a FROM at the end, like:
ALTER SEQUENCE A.sequence_name INCREMENT BY 2 FROM (SELECT * FROM information_schema.sequences) AS A
But I get a syntax error.
How can I do this?
Try using setval from docs
OR
ALTER SEQUENCE... RESTART WITH... as described here
EDITED TWICE
Sample how to change all sequences in owned schema:
do
$$
declare
i record;
begin
for i in (select * from information_schema.sequences) loop
execute $e$ALTER SEQUENCE $e$||i.sequence_name||$e$ INCREMENT BY 2$e$;
end loop;
end;
$$
;
DO

how auto generate number start from specific number the first 4 # include the current year in oracle 11g

i have table which have id when iwnna to insert new record its by default generate new # increment by 1 how can i do it please
the number is begin with year and serial for ex; 20130001,20130002,20130003 and so on , when the year is end then will start 20140001,20140002,20140003
Putting aside the question why you would want to do this.
The most straightforward approach is to create an Oracle SEQUENCE object, starting at the value you want to start with, increment of 1. As an example:
CREATE SEQUENCE myseq START WITH 20130001 INCREMENT BY 1 NOCACHE ;
To make use of the sequence object to supply a value for a table on an INSERT, create a BEFORE INSERT trigger
CREATE TRIGGER mytable_trg_bi
BEFORE INSERT ON mytable
FOR EACH ROW
BEGIN
IF NEW.mycol IS NULL THEN
SELECT myseq.NEXTVAL FROM DUAL INTO NEW.mycol;
END IF;
END;
/
(It's been a while since I've worked with the Oracle syntax; some of the keywords might not be in the right order. But this is the normal pattern for assigning unique, system generated values to a column on an INSERT.
That part is easy.
The trickier part is getting the sequence to "jump" to a specific value.
There's a couple of approaches to doing that. One would be drop the sequence object and re-create a new object, with the same name, with a new starting value. But that's not very elegant, and fairly disruptive.
Another approach is to modify the increment value, select nextval to make it jump, and then set the increment back to 1.
As a rough outline of what that might look like:
DECLARE
ln_val NUMBER;
BEGIN
-- retrieve next value from sequence
SELECT myseq.NEXTVAL FROM DUAL INTO ln_val;
-- set increment so next call to nextval will "jump"
EXECUTE IMMEDIATE
'ALTER SEQUENCE myseq INCREMENT BY '|| 20140001 - 2 - ln_val ||' NOCACHE';
-- this should return us 20140000
SELECT myseq.NEXTVAL FROM DUAL INTO ln_val;
-- reset increment back to 1
EXECUTE IMMEDIATE
'ALTER SEQUENCE myseq INCREMENT BY 1';
END;
/
Note that this approach of setting/resetting the current value of the sequence is subject to a race condition, if another session is pulling values from the SEQUENCE at the same time.

Compound trigger: collecting mutating rows into nested table

I have two tables in my project: accounts and transactions (one-to-many relationship). In every transaction I store the balance of the associated account (after the transaction is executed). Additionally in every transaction I store a value of the transaction.
So I needed a trigger fired when someone adds new transaction. It should check whether new account balance will be correct (old account balance + transaction value = new account balance stored in transaction).
So I was suggested, I should use a compound trigger which would:
in before each row section: save a row's PK (made of two columns) somewhere,
in after statement section: check whether all inserted transactions where correct.
Now I can't find anywhere how could I implement the first point.
What I already have:
CREATE OR REPLACE TRIGGER check_account_balance_is_valid
FOR INSERT
ON Transactions
COMPOUND TRIGGER
TYPE Modified_transactions_T IS TABLE OF Transactions%ROWTYPE;
Modified_transactions Modified_transactions_T;
BEFORE STATEMENT IS BEGIN
Modified_transactions := Modified_transactions_T();
END BEFORE STATEMENT;
BEFORE EACH ROW IS BEGIN
Modified_transactions.extend;
Modified_transactions(Modified_transactions.last) := :NEW;
END BEFORE EACH ROW;
AFTER STATEMENT IS BEGIN
NULL; -- I will write something here later
END AFTER STATEMENT;
END check_account_balance_is_valid;
/
However, I got that:
Warning: execution completed with warning
11/58 PLS-00049: bad bind variable 'NEW'
Could someone tell me, how to fix it? Or maybe my whole "compound trigger" idea is wrong and you have better suggestions.
Update 1
Here is my ddl script: http://pastebin.com/MW0Eqf9J
Maybe try this one:
TYPE Modified_transactions_T IS TABLE OF ROWID;
Modified_transactions Modified_transactions_T;
BEFORE STATEMENT IS BEGIN
Modified_transactions := Modified_transactions_T();
END BEFORE STATEMENT;
BEFORE EACH ROW IS BEGIN
Modified_transactions.extend;
Modified_transactions(Modified_transactions.last) := :NEW.ROWID;
END BEFORE EACH ROW;
or this
TYPE PrimaryKeyRecType IS RECORD (
Col1 Transactions.PK_COL_1%TYPE, Col2 Transactions.PK_COL_2%TYPE);
TYPE Modified_transactions_T IS TABLE OF PrimaryKeyRecType;
...
Modified_transactions(Modified_transactions.last) := PrimaryKeyRecType(:NEW.PK_COL_1, :NEW.PK_COL_2);
Your immediate problem is that :new is not a real record so it is not of type Transactions%ROWTYPE. If you're really going to go down this path, you would generally want to declare a collection of the primary key of the table
TYPE Modified_transactions_T IS TABLE OF Transactions.Primary_Key%TYPE;
and then put just the primary key in the collection
BEFORE EACH ROW IS BEGIN
Modified_transactions.extend;
Modified_transactions(Modified_transactions.last) := :NEW.Primary_Key;
END BEFORE EACH ROW;
The fact that you are trying to work around a mutating table exception in the first place, however, almost always indicates that you have an underlying data modeling problem that you should really be solving. If you need to query other rows in the table in order to figure out what you want to do with the new rows, that's a pretty good indication that you have improperly normalized your data model and that one row has some dependency on another row in the same table rather than being an autonomous fact. Fixing the data model is almost always preferrable to working around the mutating table exception.