I am pretty new when it comes to SQL and I am currently working on triggers and I have a trigger i am not sure what the problem is. I was wondering if someone could help me figure out what is wrong. Truthfully I am not even sure if this is a good trigger or not. I am having a little trouble with them. Thank you. I appreciate it.
Trigger to raise error on new purchase or updated purchase
CREATE OR REPLACE TRIGGER reminder1
AFTER INSERT OR UPDATE ON PurchasedDeal
FOR EACH ROW
BEGIN
RAISE_APPLICATION_ERROR( 'Notify new Purchased Deal Created or updated' );
END;
All it is saying is this:
Warning: Trigger created with compilation errors.
Not sure which software you are using for developing your PL/SQL code. However, you should find out how to make the "compilation error" messages visible eg
using sqlcl (command line tool)
-- just a test table ...
create table purchaseddeal ( id )
as
select 1 from dual ;
CREATE OR REPLACE TRIGGER reminder1
AFTER INSERT OR UPDATE ON PurchasedDeal
FOR EACH ROW
BEGIN
RAISE_APPLICATION_ERROR( 'Notify new Purchased Deal Created or updated' );
END;
/
SQL> show error
Errors for TRIGGER ...REMINDER1:
LINE/COL ERROR
-------- --------------------------------------------------------------------------------------------
2/1 PL/SQL: Statement ignored
2/1 PLS-00306: wrong number or types of arguments in call to 'RAISE_APPLICATION_ERROR'
When using https://livesql.oracle.com you'll get
-- this will be displayed straightaway after compiling ...
Errors: TRIGGER REMINDER1
Line/Col: 2/1 PL/SQL: Statement ignored
Line/Col: 2/1 PLS-00306: wrong number or types of arguments in call to 'RAISE_APPLICATION_ERROR'
Then, you can find out more by looking at the documentation.
RAISE_APPLICATION_ERROR Procedure
You can invoke the RAISE_APPLICATION_ERROR procedure (defined in the
DBMS_STANDARD package) only from a stored subprogram or method.
Typically, you invoke this procedure to raise a user-defined exception
and return its error code and error message to the invoker.
To invoke RAISE_APPLICATION_ERROR, use this syntax:
RAISE_APPLICATION_ERROR (error_code, message[, {TRUE | FALSE}]); You
must have assigned error_code to the user-defined exception with the
EXCEPTION_INIT pragma. The syntax is:
PRAGMA EXCEPTION_INIT (exception_name, error_code) The error_code is
an integer in the range -20000..-20999 and the message is a character
string of at most 2048 bytes.
It is problably not obvious (to yourself or other programmers) why you would use a procedure called RAISE_APPLICATION_ERROR when things go to plan ie when a row was successfully INSERTed or UPDATED. Maybe the following example is a better illustration of what you wanted to code (or see):
Table: purchaseddeal
SQL> select * from purchaseddeal ;
PRODUCT_ID QTY PRICE
---------- ---------- ----------
1 10 100
Trigger
-- fires when a price is too high or too low
-- (this could also be coded as a CHECK constraint - but the question is about triggers ...).
create or replace trigger checktheprice
before insert or update on purchaseddeal
for each row
begin
if :new.price < 0 then
raise_application_error( -20000, 'Price too low' ) ;
elsif :new.price > 1000 then
raise_application_error( -20001, 'Price too high' ) ;
end if;
end ;
/
Testing
SQL> insert into purchaseddeal values ( 2, 20, 2000 ) ;
Error starting at line : 1 in command -
insert into purchaseddeal values ( 2, 20, 2000 )
Error report -
ORA-20001: Price too high
ORA-06512: at "...CHECKTHEPRICE", line 5
ORA-04088: error during execution of trigger '...CHECKTHEPRICE'
SQL> insert into purchaseddeal values ( 2, 20, -2000 ) ;
Error starting at line : 1 in command -
insert into purchaseddeal values ( 2, 20, -2000 )
Error report -
ORA-20000: Price too low
ORA-06512: at "...CHECKTHEPRICE", line 3
ORA-04088: error during execution of trigger '...CHECKTHEPRICE'
The table is still untouched ...
select * from purchaseddeal ;
SQL> select * from purchaseddeal ;
PRODUCT_ID QTY PRICE
---------- ---------- ----------
1 10 100
Anonymous block and PRAGMA EXCEPTION_INIT
declare
price_too_low exception ;
price_too_high exception ;
-- assign the error_codes to the user-defined exceptions
pragma exception_init( price_too_low, -20000 ) ;
pragma exception_init( price_too_high, -20001 ) ;
begin
insert into purchaseddeal values ( 2, 20, 2000 ) ;
-- insert into purchaseddeal values ( 2, 20, -2000 ) ;
exception
when price_too_low then
dbms_output.put_line( to_char( sqlerrm( -20000 ) ) ) ;
when price_too_high then
dbms_output.put_line( to_char( sqlerrm( -20001 ) ) ) ;
end ;
/
-- output
ORA-20001: Price too high
ORA-06512: at "...CHECKTHEPRICE", line 5
ORA-04088: error during execution of trigger '...CHECKTHEPRICE'
PL/SQL procedure successfully completed.
Some more testing: values in allowed range
-- insert some rows that contain values in the allowed range
insert into purchaseddeal ( product_id, qty, price )
select
level + 1, ( level + 1 ) * 10, ( level + 1 ) * 100.00
from dual
connect by level <= 8 ;
The table now contains ...
SQL> select * from purchaseddeal ;
PRODUCT_ID QTY PRICE
---------- ---------- ----------
1 10 100
2 20 200
3 30 300
4 40 400
5 50 500
6 60 600
7 70 700
8 80 800
9 90 900
9 rows selected.
Related
I am trying to make a Trigger to restrict the user to performing DDL on Saturday and Sunday but if someone tries to insert data it will save that that in the weekend_action table but also raise application error that cannot perform DDL.
SQL QUERY:
create or replace trigger tgr_wkd_action
before insert
on tbl_39_dept_k
for each row
declare
begin
IF trim(TO_CHAR(sysdate,'Day')) IN ('Tuesday', 'Sunday') then
RAISE_APPLICATION_ERROR (-20000,'you cannot perform DDL on Weekend');
end if;
if inserting then
insert into user_admin.weekend_actions values
(:NEW.Dept_no,
'updation',
'user'||user||'trying to insert data on'||'_'||sysdate||'from Table tbl_39_dept_k');
end if;
end tgr_wkd_action;
if someone tries to insert data
Exactly. Insert.
Your trigger fires before update, and that's a different DML.
Apart from that:
inserts and updates are DML (data manipulation). DDL you mentioned is data definition (these are create table, alter table, ...)
weekend action vs. Tuesday? Since when is Tuesday weekend?
Perhaps you meant
SQL> CREATE OR REPLACE TRIGGER tgr_wkd_action
2 BEFORE INSERT OR UPDATE
3 ON tbl_39_dept_k
4 FOR EACH ROW
5 DECLARE
6 BEGIN
7 IF TRIM (TO_CHAR (SYSDATE, 'Day', 'nls_date_language = english')) IN
8 ('Saturday', 'Sunday')
9 THEN
10 RAISE_APPLICATION_ERROR (-20000, 'you cannot perform DML on Weekend');
11 END IF;
12
13 IF INSERTING
14 THEN
15 INSERT INTO weekend_actions
16 VALUES (
17 :NEW.Dept_no,
18 'inserting',
19 'user'
20 || USER
21 || 'trying to insert data on'
22 || '_'
23 || SYSDATE
24 || 'from Table tbl_39_dept_k');
25 ELSIF UPDATING
26 THEN
27 INSERT INTO weekend_actions
28 VALUES (
29 :NEW.Dept_no,
30 'updating',
31 'user'
32 || USER
33 || 'trying to update data on'
34 || '_'
35 || SYSDATE
36 || 'from Table tbl_39_dept_k');
37 END IF;
38 END tgr_wkd_action;
39 /
Trigger created.
SQL>
SQL> INSERT INTO tbl_39_dept_k (dept_no) VALUES (10);
1 row created.
SQL>
Tables' contents:
SQL> select * from tbl_39_dept_k;
DEPT_NO
----------
10
SQL> select * from weekend_actions;
DEPT_NO ACTION MSG
---------- ---------- ----------------------------------------------------------------------
10 inserting userSCOTTtrying to insert data on_23.11.21from Table tbl_39_dept_k
SQL>
Pretending it is weekend today (while it is Tuesday):
<snip>
7 IF TRIM (TO_CHAR (SYSDATE, 'Day', 'nls_date_language = english')) IN
8 ('Tuesday', 'Sunday')
9 THEN
<snip>
39 /
Trigger created.
SQL>
SQL> INSERT INTO tbl_39_dept_k (dept_no) VALUES (20);
INSERT INTO tbl_39_dept_k (dept_no) VALUES (20)
*
ERROR at line 1:
ORA-20000: you cannot perform DML on Weekend
ORA-06512: at "SCOTT.TGR_WKD_ACTION", line 6
ORA-04088: error during execution of trigger 'SCOTT.TGR_WKD_ACTION'
SQL>
Please help me. I am new to PL SQL. I want to Display On Screen all these details
Customer.customerNo, Customer.customerName, Customer.custBalance, customerOrder.orderDate, customerOrder.orderNo
from 2 tables [Customer, CustomerOrder]. Using the only Package with Procedure. Please help me how can I create such a package?
create or replace package my_pkg as
Procedure getAllOrders2(customer_id IN varchar2);
end my_pkg;
/
create or replace package body my_pkg as
Procedure getAllOrders2(customer_id IN varchar2) is
begin
dbms_output.put_line('customer_id is: '||customer_id );
end getAllOrders2;
end my_pkg;
/
Output Should be:
Customer Number, Customer Name, Order Number, Order Date, Customer Balance
Here is the output of all tables
One option is to use a cursor FOR loop (why? Because customer can have more than a single order, and - if you use a single select statement - you might get the too_many_rows error so you'd have to select into a collection or - as in my example - use a loop.
SQL> create or replace procedure getallorders2 (par_customer_id in customer.customerno%type)
2 is
3 begin
4 for cur_r in (select c.customerno,
5 c.customername,
6 c.custbalance,
7 o.orderdate,
8 o.orderno
9 from customer c join customerorder o
10 on c.customerno = o.customerno
11 where c.customerno = par_customer_id
12 )
13 loop
14 dbms_output.put_line
15 (cur_r.customerno ||', '||
16 cur_r.customername ||', '||
17 cur_r.custbalance ||', '||
18 cur_r.orderno ||', '||
19 to_char(cur_r.orderdate, 'dd.mm.yyyy')
20 );
21 end loop;
22 end;
23 /
Procedure created.
Testing:
SQL> set serveroutput on;
SQL> begin
2 getallorders2('A101');
3 end;
4 /
A101, basant, 32000, O101, 04.02.2021
PL/SQL procedure successfully completed.
SQL>
Now that you know how, move that code into a package body.
Here is an example:
CREATE TABLE #t
(
id int IDENTITY(1,1) PRIMARY KEY,
ss varchar(50)
)
INSERT INTO #t (ss)
SELECT name FROM master.dbo.spt_values
DELETE FROM #t
Run it and check wait stats:
SELECT * FROM sys.dm_exec_session_wait_stats WHERE session_id = <spid>
session_id wait_type waiting_tasks_count wait_time_ms max_wait_time_ms signal_wait_time_ms
---------- ------------------------------ -------------------- -------------------- -------------------- --------------------
116 SOS_SCHEDULER_YIELD 2 0 0 0
116 MEMORY_ALLOCATION_EXT 125 0 0 0
Now put the same code inside stored procedure:
CREATE PROCEDURE dbo.tp_test_temp_latches
AS
BEGIN
CREATE TABLE #t
(
id int IDENTITY(1,1) PRIMARY KEY,
ss varchar(50)
)
INSERT INTO #t (ss)
SELECT name FROM master.dbo.spt_values
DELETE FROM #t
END
Run it and check wait stats again.
We will see a lot of pagelatch_ex waits:
session_id wait_type waiting_tasks_count wait_time_ms max_wait_time_ms signal_wait_time_ms
---------- ----------------------------- -------------------- -------------------- -------------------- --------------------
1043 PAGELATCH_EX 2537 5 0 3
1043 SOS_SCHEDULER_YIELD 2 0 0 0
1043 MEMORY_ALLOCATION_EXT 132 1 0 0
I checked execution plan with DBCC TRACEON (8666).
First example shows RowSetSharing = 1.
Second example shows RowSetSharing = 0.
I have some big stored procedures which works a lot with temp tables in my current project. I would like to take benefit from rowset sharing but I can't. I founded Paul White blog post where he describes how he turn off rowset sharing with OPTION (QUERYTRACEON 8746): changes-to-a-writable-partition-may-fail
Is there any option for turning it on? Or any other possibility?
UPDATE 05/03/2018
I have found one workaround.
To eliminate excesive PAGELATCH_EX waits we could turn on "Table spool" by using trace flag 8692:
DELETE FROM #t
OPTION (QUERYTRACEON 8692)
This option allows to read all needed rows and only than to update/delete them. So we separate read/write operations. This technique eliminates PAGELATCH_EX waits and drops logical reads almost twice. This is still far from using temp table outside stored procedure. But it is twice faster than not using this option.
UPDATE 07/03/2018
Further investigation leaded me to one more workaround (more efficient):
DELETE t FROM (SELECT TOP (10000000) * FROM #t ORDER BY IIF(id = 0, 0, id)) t
OPTION (MAXDOP 1)
Sort operator works in memory instead in tempdb like "table spool" does.
So I have two tables:
CREATE TABLE SALE(
OID_PA NUMBER(*,0) PRIMARY KEY,
Amount INTEGER NOT NULL,
Delivery_DATE DATE NOT NULL,
OID_V NUMBER(*,0) NOT NULL,
CONSTRAINT "AMOUNTCHECK" CHECK(Amount >=0),
FOREIGN KEY (OID_V) REFERENCES TICKET(OID_V));
and
CREATE TABLE PRODUCT
( Code NUMBER(*,0) PRIMARY KEY,
Stock INTEGER NOT NULL,
Price NUMBER(4,2) NOT NULL,
Production_Cost NUMBER NOT NULL,
Model VARCHAR2(50) NOT NULL,
TipoMueble VARCHAR2(50) NOT NULL,
TipoMaterial VARCHAR2(50) NOT NULL,
OID_PA NUMBER(*,0) NOT NULL,
CONSTRAINT "TipoMueble" CHECK(TipoMueble IN
('Canteado','Complementos','Glaciar',
'GrupoDiseño','Tiradores','Vitrinas')),
CONSTRAINT "TipoMaterial" CHECK(TipoMaterial IN ('Aluminio','Lacados',
'Polilaminado','Madera','Cristal','Chapa','Granito','Formica',
'Aglomerado','Marmol','Elementos de ferreteria')),
CONSTRAINT "StockNegativo" CHECK(Stock >=0),
CONSTRAINT "Precio" CHECK(Price>=0),
CONSTRAINT "CosteProduccion" CHECK (Production_cost>=0),
FOREIGN KEY (OID_PA) REFERENCES PAQUETE(OID_PA));
And now I want to create a trigger, the moment I modify the attribute "Amount" in the table "SALE" then it should check the table "PRODUCT".
if "stock" (in PRODUCT) is less than Amount (in SALE) then
RAISE_APPLICATION_ERROR(-20003, 'Not enough stock');
But I don't know how to make the trigger check in both tables at the same time.
right now my code looks likethis right now, it also has some errors that I'm not sure how to fix.
CREATE OR REPLACE TRIGGER TR_ENOUGH_STOCK
AFTER INSERT OR UPDATE OF Amount ON SALE
BEGIN
SELECT a.Amount, b.Stock
FROM ( SALE a inner join PRODUCT b on (a.Code = b.Code))
IF b.Stock < a.Amount THEN
RAISE APPLICATION ERROR(-20003, 'Not enough stock');
END IF;
END;
/
And I'm getting the following errors:
Error(209,5): PL/SQL: SQL Statement ignored
Error(212,5): PL/SQL: ORA-00933: SQL command not properly ended
Error(214,9): PLS-00103: Encountered the symbol "IF" when expecting one of
the following: ; <an identifier> <a double-quoted delimited-identifier>
Here's an example of how you might do that.
Create tables & a trigger:
SQL> create table sale (oid_pa number, amount number);
Table created.
SQL> create table product (code number, stock number, oid_pa number);
Table created.
SQL>
SQL> create or replace trigger trg_biu_stock
2 before insert or update
3 on sale
4 for each row
5 declare
6 l_stock product.stock%type;
7 begin
8 select stock into l_stock
9 from product
10 where oid_pa = :new.oid_pa;
11
12 if :new.amount > l_stock then
13 raise_application_error(-20000, 'Not enough stock');
14 end if;
15 end;
16 /
Trigger created.
SQL>
Insert sample records into the PRODUCT table:
SQL> insert into product values (1, 100, 10);
1 row created.
SQL> insert into product values (2, 50, 20);
1 row created.
SQL> select * From product;
CODE STOCK OID_PA
---------- ---------- ----------
1 100 10
2 50 20
SQL>
Let's test it:
SQL> -- 90 is less OID_PA = 10 stock value (which is 100)
SQL> insert into sale values (10, 90);
1 row created.
SQL> -- let's try to update it to a value larger than stock value:
SQL> update sale set amount = 200 where oid_pa = 10;
update sale set amount = 200 where oid_pa = 10
*
ERROR at line 1:
ORA-20000: Not enough stock
ORA-06512: at "HR.TRG_BIU_STOCK", line 9
ORA-04088: error during execution of trigger 'HR.TRG_BIU_STOCK'
SQL> -- how about a value which doesn't exceed the stock value?
SQL> update sale set amount = 5 where oid_pa = 10;
1 row updated.
SQL> select * From sale;
OID_PA AMOUNT
---------- ----------
10 5
SQL>
As of your attempt: apart from trigger code having wrong syntax, it wouldn't work anyway because you can't reference a table trigger is based on because it is right now being changed, i.e. it is mutating. Oracle raises an error in such cases (see an example below). The correct way is to reference that table's columns using :new or :old, as I did (see an example above).
SQL> create or replace trigger trg_biu_stock
2 before insert or update
3 on sale
4 for each row
5 declare
6 l_stock product.stock%type;
7 begin
8 select p.stock into l_stock
9 from product p
10 join sale s
11 on s.oid_pa = p.oid_pa
12 where p.oid_pa = :new.oid_pa;
13
14 if :new.amount > l_stock then
15 raise_application_error(-20000, 'Not enough stock');
16 end if;
17 end;
18 /
Trigger created.
SQL> update sale set amount = 50 where oid_pa = 10;
update sale set amount = 50 where oid_pa = 10
*
ERROR at line 1:
ORA-04091: table HR.SALE is mutating, trigger/function may not see it
ORA-06512: at "HR.TRG_BIU_STOCK", line 4
ORA-04088: error during execution of trigger 'HR.TRG_BIU_STOCK'
SQL>
I have a table in oracle of which all rows of a column has to be updated with a starting value of 500 and incrementing by 1.
I tried to find something similar online but wasn't able to get anything useful. oracle and PL/SQL is not my expertise. any help would be appreciated.
I won't use PL/SQL since it could be done in plain SQL.
You could use a SEQUENCE starting with 500 and incremented by 1.
For example,
set up
SQL> DROP SEQUENCE s ;
Sequence dropped.
SQL>
SQL> CREATE SEQUENCE s START WITH 500 INCREMENT BY 1;
Sequence created.
SQL>
SQL> DROP TABLE t PURGE;
Table dropped.
SQL>
SQL> CREATE TABLE t AS SELECT LEVEL id FROM dual CONNECT BY LEVEL < =20;
Table created.
SQL>
SQL> SELECT * FROM t;
ID
----------
1
2
3
4
5
6
7
8
9
10
11
ID
----------
12
13
14
15
16
17
18
19
20
20 rows selected.
SQL>
Now, let's update the table with the sequence.
SQL> UPDATE t SET ID = s.nextval;
20 rows updated.
SQL>
SQL> SELECT * FROM t;
ID
----------
500
501
502
503
504
505
506
507
508
509
510
ID
----------
511
512
513
514
515
516
517
518
519
20 rows selected.
SQL>
So, you have all the rows updated with the sequence starting with 500 and incremented by 1.
Please try like this,
DECLARE
VAL = 500;
BEGIN
FOR REC IN ( SELECT
*
FROM
Table1
)
LOOP
UPDATE Table1 SET col1 = VAL WHERE COL1 = REC.COL1 ;
VAL = VAL +1;
END LOOP;
END;
DECLARE
VAL NUMBER := 1;
BEGIN
FOR REC IN ( select *
from customers
)
LOOP
UPDATE customers SET ID1 = VAL WHERE ID = REC.ID ;
VAL := VAL +1;
END LOOP;
END;