so I made a trigger insert like this:
create or replace trigger discount
after insert on transaction
for each row
begin
if (new.desc_date = 'Y') then
insert into desc_transaction(discount) values (new.discount = '0.1');
end if;
end;
/
Warning: Trigger created with compilation errors.
show error;
3/1 PL/SQL: SQL Statement ignored
3/54 PL/SQL: ORA-00917: missing comma
how to solve it.
data :
desc_date contains only yes and no because if November is discounted and October is not.
For the discount section contains 0.1 and null so if desc_date is yes then 0.1 otherwise it will be null or ' '.
Wrong syntax. Should've been
CREATE OR REPLACE TRIGGER discount
AFTER INSERT
ON transaction
FOR EACH ROW
BEGIN
IF :new.desc_date = 'Y'
THEN
INSERT INTO desc_transaction (discount)
VALUES (0.1);
END IF;
END;
/
Or, possibly even better,
CREATE OR REPLACE TRIGGER discount
AFTER INSERT
ON transaction
FOR EACH ROW
BEGIN
INSERT INTO desc_transaction (discount)
VALUES (CASE WHEN :new.desc_date = 'Y' THEN 0.1 ELSE NULL END);
END;
/
The syntax of your insert statement is incorrect. Try the following:
insert into desc_transaction(discount) values ('0.1');
Side note - discount probably isn't a varchar column. If this guess is correct, you should be using a numeral literal (0.1) and not a string literal ('0.1') like you currently have.
Related
Hello fellow programmers and happy new year to you all!
I have few university tasks for winter break and one of them is to create trigger on table:
PERSON(ID, Name, Surname, Age);
Trigger is supposed to inform user when they have inserted row with invalid ID. Vadility criteria is that ID is 11 digits long.
I tried to write solution like this:
CREATE OR REPLACE TRIGGER person_id_trigg
AFTER INSERT
ON person
DECLARE
idNew VARCHAR(50);
lengthException EXCEPTION;
BEGIN
SELECT id INTO idNew FROM INSERTED;
IF LENGTH(idNew) <> 11 THEN
RAISE lengthException;
END IF;
EXCEPTION
WHEN lengthException THEN
dbms_output.put_line('ID for new person is INVALID. It must be 11 digits long!');
END;
Then I realized that INSERTED exists only in sqlserver and not in oracle.
What would you suggest I could do to fix that?
Thanks in advance!
Do you want to raise an exception (which would prevent the insert from succeeding)? Or do you want to allow the insert to succeed and write a string to the dbms_output buffer that may or may not exist and may or may not be shown to a human running the insert?
In either case, you'll want this to be a row-level trigger, not a statement-level trigger, so you'll need to add the for each row clause.
CREATE OR REPLACE TRIGGER person_id_trigg
AFTER INSERT
ON person
FOR EACH ROW
If you want to raise an exception
BEGIN
IF( length( :new.id ) <> 11 )
THEN
RAISE_APPLICATION_ERROR( -20001,
'The new ID value must have a length of 11' );
END IF;
END;
If you want to potentially print output but allow the insert to succeed
BEGIN
IF( length( :new.id ) <> 11 )
THEN
dbms_output.put_line( 'The new ID value must have a length of 11' );
END IF;
END;
Of course, in reality, you would never use a trigger for this sort of thing. In the real world, you would use a constraint.
I have a simple test function where I'm passing in a specific ID (the primary key of the table I'm selecting from), and computing a simple function on it and the parameters.
The skeleton code and test:
create or replace function test(id varchar2, area float) return float is
theRow forest%ROWTYPE;
begin
select * into theRow from forest where Forest_No = id;
return area / theRow.Area;
end;
begin
select test('1', 16000) from dual;
end;
The output:
[2019-10-14 21:19:10] [65000][6550] ORA-06550: line 2, column 5:
[2019-10-14 21:19:10] PLS-00428: an INTO clause is expected in this SELECT statement
I am at a loss for what to do here, as far as I can tell the documentation and examples use the same order and syntax. I have tried moving the into clause to the end as in Postgresql, but that did not work.
What have I missed here?
Issue is in calling statement.
Whenever select statement is used in plsql block it must have into clause to assign return value to variable.
You should remove begin and end from your calling code:
--begin -- remove this
select test('1', 16000) from dual;
--end; -- remove this
Or if you want to use it in plsql block then add into clause:
Declare
Area_ float(precision);
begin
select test('1', 16000) into area_ from dual;
-- use area_ in your code wherever required
dbms_output.put_line('area: ' || area_);
end;
Cheers!!
I have 2 tables, Contract and Bankslip.
I need to get the date field from the Contract table, and set the date on Bankslip table, but it's getting in a loop, I think!
How can i do it?
Here is my code:
create or replace TRIGGER GFLANCAM_ATUALIZA_DATA_EMISSAO
BEFORE INSERT ON GFLANCAM
FOR EACH ROW
DECLARE
DATA_INICIO_CONTRATO DATE;
BEGIN
CASE WHEN :NEW.DOCUMENTO <> ' ' then
SELECT dt_inicio
INTO DATA_INICIO_CONTRATO
from ctcontra
where cd_contrato = :NEW.documento;
:NEW.data := DATA_INICIO_CONTRATO;
END CASE;
END;
What am I doing wrong?
Much of the trigger is unnecessary.
You can accomplish your goal without the CASE and without defining a variable.
CREATE OR REPLACE TRIGGER GFLANCAM_ATUALIZA_DATA_EMISSAO
BEFORE INSERT
ON GFLANCAM
FOR EACH ROW
BEGIN
-- Consider following:
-- IF NVL (:NEW.DOCUMENTO, ' ') <> ' '
IF :NEW.DOCUMENTO <> ' '
THEN
-- Following line may cause ORA-01403: no data found
SELECT dt_inicio INTO :NEW.data FROM ctcontra WHERE cd_contrato = :NEW.documento;
END IF;
END;
/
A few notes:
If you want to catch NULL values then add the NVL shown above.
Watch out for the case where a corresponding record is not found in ctcontra--this condition would result in ORA-01403: no data found (which might be exactly what you want in this case).
Make sure that ctcontra has only one record for each cd_contrato value, otherwise you will get a ORA-01422: exact fetch returns more than requested number of rows.
Take a look at the update:
{CREATE OR REPLACE TRIGGER GFLANCAM_ATUALIZA_DATA_EMISSAO
AFTER INSERT ON GFLANCAM
FOR EACH ROW
DECLARE
DATA_INICIO_CONTRATO DATE;
BEGIN
IF DOCUMENTO <> ' ' THEN
SELECT dt_inicio INTO DATA_INICIO_CONTRATO from ctcontra where cd_contrato =
DOCUMENTO;
UPDATE GFLANCAM SET DATA = DATA_INICIO_CONTRATO;
END IF;
END;}
I am trying to implement a statement level trigger to enforce the following "An applicant cannot apply for more than two positions in one day".
I am able to enforce it using a row level trigger (as shown below) but I have no clue how to do so using a statement level trigger when I can't use :NEW or :OLD.
I know there are alternatives to using a trigger but I am revising for my exam that would have a similar question so I would appreciate any help.
CREATE TABLE APPLIES(
anumber NUMBER(6) NOT NULL, /* applicant number */
pnumber NUMBER(8) NOT NULL, /* position number */
appDate DATE NOT NULL, /* application date*/
CONSTRAINT APPLIES_pkey PRIMARY KEY(anumber, pnumber)
);
CREATE OR REPLACE TRIGGER app_trigger
BEFORE INSERT ON APPLIES
FOR EACH ROW
DECLARE
counter NUMBER;
BEGIN
SELECT COUNT(*) INTO counter
FROM APPLIES
WHERE anumber = :NEW.anumber
AND to_char(appDate, 'DD-MON-YYYY') = to_char(:NEW.appDate, 'DD-MON-YYYY');
IF counter = 2 THEN
RAISE_APPLICATION_ERROR(-20001, 'error msg');
END IF;
END;
You're correct that you don't have :OLD and :NEW values - so you need to check the entire table to see if the condition (let's not call it a "constraint", as that term has specific meaning in the sense of a relational database) has been violated:
CREATE OR REPLACE TRIGGER APPLIES_AIU
AFTER INSERT OR UPDATE ON APPLIES
BEGIN
FOR aRow IN (SELECT ANUMBER,
TRUNC(APPDATE) AS APPDATE,
COUNT(*) AS APPLICATION_COUNT
FROM APPLIES
GROUP BY ANUMBER, TRUNC(APPDATE)
HAVING COUNT(*) > 2)
LOOP
-- If we get to here it means we have at least one user who has applied
-- for more than two jobs in a single day.
RAISE_APPLICATION_ERROR(-20002, 'Applicant ' || aRow.ANUMBER ||
' applied for ' || aRow.APPLICATION_COUNT ||
' jobs on ' ||
TO_CHAR(aRow.APPDATE, 'DD-MON-YYYY'));
END LOOP;
END APPLIES_AIU;
It's a good idea to add an index to support this query so it will run efficiently:
CREATE INDEX APPLIES_BIU_INDEX
ON APPLIES(ANUMBER, TRUNC(APPDATE));
dbfiddle here
Best of luck.
Your rule involves more than one row at the same time. So you cannot use a FOR ROW LEVEL trigger: querying on APPLIES as you propose would hurl ORA-04091: table is mutating exception.
So, AFTER statement it is.
CREATE OR REPLACE TRIGGER app_trigger
AFTER INSERT OR UPDATE ON APPLIES
DECLARE
cursor c_cnt is
SELECT 1 INTO counter
FROM APPLIES
group by anumber, trunc(appDate) having count(*) > 2;
dummy number;
BEGIN
open c_cnt;
fetch c_cnt in dummy;
if c_cnt%found then
close c_cnt;
RAISE_APPLICATION_ERROR(-20001, 'error msg');
end if;
close c_cnt;
END;
Obviously, querying the whole table will be inefficient at scale. (One of the reasons why triggers are not recommended for this sort of thing). So this is a situation in which we might want to use a compound trigger (assuming we're on 11g or later).
I'm very new for trigger, now this what i was trying. I've two tables INSERTED and ORDER_INFO, both have the same column name ORDER_ID, ORDER_DATE. I've scenario, where client will be placing his/her order then, order information will be stored into INSERTED table, then by using this trigger, it'll insert into another table ORDER_INFO after satisfying the condition, which has been written.
create trigger tri_check
AFTER INSERT ON inserted FOR EACH ROW
DECLARE
v_date DATE;
BEGIN
SELECT order_date INTO v_date FROM inserted;
if (v_date)< (sysdate + 2) then
raiserror('You cannot take an order to be delivered less than 2 days from now',16, 1);
else
INSERT INTO orders_info
( order_id,order_date)
VALUES
(:new.order_id,v_date);
end if;
end;
But, when i'm executing the above trigger, then i'm getting this error.
ERROR at line 8: PL/SQL: SQL Statement ignored
6. SELECT order_date INTO v_date FROM inserted;
7. if (v_date)< (sysdate + 2) then
8. raiserror('You cannot take an order to be delivered less than 2 days from now',16, 1);
9. else
10. INSERT INTO orders_info
EDIT
Now, i made the same structure table into SYSTEM user, and got the same error. Table or View does not exist
Need help !! Thanks in advance !!
The message seems to indicate a problem with the 'raiserror' procedure. I'm not familiar with such a procedure in standard PL/SQL - did you mean RAISE_APPLICATION_ERROR? However, and perhaps more to the point, when using a trigger there's no need to do a SELECT from the table. All the data being inserted is available to the trigger. I suggest changing your trigger to be something like the following:
create trigger tri_check
AFTER INSERT ON inserted
REFERENCING NEW AS NEW
FOR EACH ROW
BEGIN
if :new.ORDER_DATE < sysdate + INTERVAL '2' DAY then
RAISE_APPLICATION_ERROR(-20000, 'You cannot take an order to be delivered less than 2 days from now');
else
INSERT INTO orders_info
(order_id, order_date)
VALUES
(:new.order_id, :new.ORDER_DATE);
end if;
end TRI_CHECK;
Share and enjoy.
You can just use the :NEW and :OLD values instead of your select:
CREATE TRIGGER tri_check
AFTER INSERT
ON inserted
FOR EACH ROW
DECLARE
BEGIN
IF :new.order_date < (SYSDATE + 2)
THEN
raiserror (
'You cannot take an order to be delivered less than 2 days from now',
16,
1);
ELSE
INSERT INTO orders_info (order_id, order_date)
VALUES (:new.order_id, :new.order_date);
END IF;
END;
What is your raiserror procedure? Do you have access permissions granted on it?
Hope it helps...
EDIT:
OK, from your error, and the error you posted on #Bob Jarvis' answer, you might not have INSERT privilege on the ORDERS_INFO table. You also should check your permissions on the INSERTED table too.
Check your permissions with your DBA.
If raiserror is not a defined procedure or you don't have access to it then use the RAISE_APPLICATION_ERROR method for raising an error as Bob suggests.