When I ran this code, I've got this error:
PLS-00103: Encountered the symbol "IS" when expecting one of the following:
:= . ( # % ; not null range default character.
I think something wrong with declaration section but the same declaration works in a row-lvl trigger, so I'm confused is this permitted in compound triggers at all. I couldn't find the examples, so I ask you here.
create or replace trigger ivan_moving
for update of country on clients
compound trigger
declare
n_country clients.country%type;
min_date clients.bday%type;
cursor ivans is select* from clients where fname like 'Ivan%' and Bday > (select min(Bday) from clients where fname like 'Ivan%');
client_ivan ivans%rowtype;
before statement is
min_date = select Bday from clients where fname like 'Ivan%' and Bday = (select min(Bday) from clients where fname like 'Ivan%');
end before statement;
after each row is
begin
if :OLD.country <> :NEW.country and fname = 'Ivan%' and bday = min_date
n_country := :new.country;
end if;
end after each row;
after statement is
begin
open ivans;
loop
fetch ivans into client_ivan;
exit when ivans%notfound;
update clients set country = n_country where id = client_ivan.id;
end loop;
close ivans;
end after statement;
end ivan_moving;
/
Quite a few mistakes, e.g.
you shouldn't use declare
in PL/SQL, SELECT requires INTO
it is not = 'Ivan%' but like 'Ivan%'
it seems you "forgot" to specify :new for some columns
There might be some other errors I didn't mention. I don't know if trigger does what you intended, but - at least, syntax errors are now fixed.
SQL> create or replace trigger ivan_moving
2 for update of country on clients
3 compound trigger
4 n_country clients.country%type;
5 min_date clients.bday%type;
6 cursor ivans is
7 select * from clients
8 where fname like 'Ivan%'
9 and bday > min_date;
10 client_ivan ivans%rowtype;
11
12 before statement is
13 begin
14 select bday
15 into min_date
16 from clients
17 where fname like 'Ivan%'
18 and bday = (select min(bday)
19 from clients
20 where fname like 'Ivan%');
21 end before statement;
22
23 after each row is
24 begin
25 if :old.country <> :new.country
26 and :new.fname like 'Ivan%'
27 and :new.bday = min_date
28 then
29 n_country := :new.country;
30 end if;
31 end after each row;
32
33 after statement is
34 begin
35 open ivans;
36 loop
37 fetch ivans into client_ivan;
38 exit when ivans%notfound;
39 update clients set country = n_country where id = client_ivan.id;
40 end loop;
41 close ivans;
42 end after statement;
43
44 end ivan_moving;
45 /
Trigger created.
Related
CREATE TABLE user_man_sys(
user_id INT NOT NULL,
user_name NVARCHAR2(20) NOT NULL,
user_password INT ,
created_date DATE ,
PRIMARY KEY(user_id)
);
CREATE OR REPLACE PACKAGE mypackage AS
PROCEDURE add_user(
u_id user_man_sys.user_id%type,
u_name user_man_sys.user_name%type,
u_password user_man_sys.user_password%type,
u_created_data user_man_sys.created_date%type);
PROCEDURE Checkuserlogin(new_username user_man_sys.user_name%type,
new_password user_man_sys.user_password%type )
PROCEDURE rest_password ( r_password user_man_sys.user_password%type)
END mypackage ;
/
CREATE OR REPLACE PACKAGE BODY mypackage AS
PROCEDURE add_user(
u_id user_man_sys.user_id%type,
u_name user_man_sys.user_name%type,
u_password user_man_sys.user_password%type,
u_created_data user_man_sys.created_date%type)
IS
BEGIN
INSERT INTO user_man_sys(user_id,user_name ,user_password ,created_date)
VALUES(u_id, u_name,u_password,u_created_data );
END add_user;
PROCEDURE Checkuserlogin(new_usernameuser_man_sys.user_name%type,
new_password user_man_sys.user_password%type)
IS
BEGIN
SET NOCOUNT ON
SELECT CASE WHEN EXISTS(SELECT NULL FROM user_man_sysWHERE user_id=new_username AND user_password=new_password)
THEN CAST(1 AS BIT)
ELSE CAST(0 AS BIT)
END Checkuserlogin;
PROCEDURE rest_password ( r_password user_man_sys.user_password%type,r_user user_man_sys.user_name%type)
IS
BEGIN
UPDATE credentials
SET password = r_password -- use as parameter in procedure
WHERE username = r_user;
-- or you can include "and id = p_id" in where clause
IF SQL%ROWCOUNT > 0
THEN
COMMIT;
RETURN 'Password Reset Successful.';
ELSE
RETURN 'Password Reset Failed. Invalid User Name';
END IF;
END rest_password ;
end mypackage;
/
My code performes these tasks :
1)procedure to create user.
2)procedure to reset password.
3)procedure to check login cardinals.
I want to repair this error in this code if i ceate a package or the body of the package shows a mistacke (the package or the body of the the package are configured with translation errors)
How can i fix it?
Quite a few errors.
Table is created with no problems:
SQL> CREATE TABLE user_man_sys
2 (
3 user_id INT NOT NULL,
4 user_name NVARCHAR2 (20) NOT NULL,
5 user_password INT,
6 created_date DATE,
7 PRIMARY KEY (user_id)
8 );
Table created.
This is a table that's getting updated; I don't have it, so I'm creating a dummy one, just to make package body compile:
SQL> CREATE TABLE credentials
2 (
3 password VARCHAR2 (10),
4 username VARCHAR2 (20)
5 );
Table created.
Package specification: you're missing semi-colons (as terminators); description of procedures in body must match the ones in specification (rest_password is wrong, then; also, as it can't return anything (it is not a function, I added an out parameter):
SQL> CREATE OR REPLACE PACKAGE mypackage
2 AS
3 PROCEDURE add_user (u_id user_man_sys.user_id%TYPE,
4 u_name user_man_sys.user_name%TYPE,
5 u_password user_man_sys.user_password%TYPE,
6 u_created_data user_man_sys.created_date%TYPE);
7
8 PROCEDURE Checkuserlogin (new_username user_man_sys.user_name%TYPE,
9 new_password user_man_sys.user_password%TYPE);
10
11 PROCEDURE rest_password (r_password user_man_sys.user_password%TYPE,
12 r_user user_man_sys.user_name%TYPE,
13 par_result OUT VARCHAR2);
14 END mypackage;
15 /
Package created.
Package body: typos (missing spaces); set belongs to SQL*Plus, not PL/SQL procedures; Checkuserlogin's select statement requires an into clause and valid datatype (bit isn't):
SQL> CREATE OR REPLACE PACKAGE BODY mypackage
2 AS
3 PROCEDURE add_user (u_id user_man_sys.user_id%TYPE,
4 u_name user_man_sys.user_name%TYPE,
5 u_password user_man_sys.user_password%TYPE,
6 u_created_data user_man_sys.created_date%TYPE)
7 IS
8 BEGIN
9 INSERT INTO user_man_sys (user_id,
10 user_name,
11 user_password,
12 created_date)
13 VALUES (u_id,
14 u_name,
15 u_password,
16 u_created_data);
17 END add_user;
18
19 PROCEDURE Checkuserlogin (new_username user_man_sys.user_name%TYPE,
20 new_password user_man_sys.user_password%TYPE)
21 IS
22 l_val NUMBER;
23 BEGIN
24 --SET NOCOUNT ON
25
26 SELECT CASE
27 WHEN EXISTS
28 (SELECT NULL
29 FROM user_man_sys
30 WHERE user_id = new_username
31 AND user_password = new_password)
32 THEN
33 1
34 ELSE
35 0
36 END
37 INTO l_val
38 FROM DUAL;
39 END Checkuserlogin;
40
41 PROCEDURE rest_password (r_password user_man_sys.user_password%TYPE,
42 r_user user_man_sys.user_name%TYPE,
43 par_result OUT VARCHAR2)
44 IS
45 BEGIN
46 UPDATE credentials
47 SET password = r_password -- use as parameter in procedure
48 WHERE username = r_user;
49
50 -- or you can include "and id = p_id" in where clause
51 IF SQL%ROWCOUNT > 0
52 THEN
53 COMMIT;
54 par_result := 'Password Reset Successful.';
55 ELSE
56 par_result := 'Password Reset Failed. Invalid User Name';
57 END IF;
58 END rest_password;
59 END mypackage;
60 /
Package body created.
SQL>
That's it, as far as compilation problems is concerned. Does your code do what you planned, I wouldn't know. Test it yourself.
I have to check that the update of a note is not inferior to 10% with a trigger. if it is greater than 10% I have to adjust it to 10% and write an error message , and if the new note is under 0 i have to set it to 0
when i try raise_application error, the update is just cancel.
and my order option DBMS does not work the update is done with the good value but without message
CREATE OR REPLACE TRIGGER C3_update
BEFORE UPDATE OF note on Inscription
FOR EACH ROW
WHEN (NEW.note < OLD.note*0.9)
begin
if(:NEW.note > 0) then
:NEW.note := :OLD.note*0.9;
dbms_output.enable;
dbms_output.put_line ('la note ne peut descendre de plus de 10%');
--or
--RAISE_APPLICATION_ERROR(-20111,'the note can not be less than 10%');
end if;
if(:NEW.note < 0) then
:NEW.note := 0;
RAISE_APPLICATION_ERROR(-20011,'the note can not be under 0');
end if;
end;
/
i would like to set the note and print message error rigth now i just cancel the update or i update the note in the good way but the message do not print
From my point of view, this is a wrong approach. Business rule shouldn't be enforced by a database trigger. If I were you, I'd put it into a front-end application (the same place where a new note value is being entered).
Doing so, you'd even be able to notify user what's happening and let them decide whether they want to accept a new value (which you'd suggest, based on those conditions), or not (and, possibly, enter a new, valid value).
As you've noticed, DBMS_OUTPUT.PUT_LINE doesn't work. It would, if tool you use to update the note value is capable of displaying it. (By the way, did you SET SERVEROUTPUT ON?) If you're using, for example, Oracle Forms, then you'd see nothing.
RAISE_APPLICATION_ERROR, when called, ends the subprogram and returns a user-defined error number and message. However, it also nullifies the update you make. Even if you set the trigger to be an autonomous transaction, it won't work. For example:
SQL> drop table inscription;
Table dropped.
SQL> create table inscription (note number);
Table created.
SQL> insert into inscription (note) values (2);
1 row created.
SQL> create or replace trigger c3_update
2 before update of note on inscription
3 for each row
4 when (new.note < old.note * 0.9)
5 declare
6 pragma autonomous_transaction;
7 l_note inscription.note%type;
8 l_info varchar2(100);
9 begin
10 if :new.note > 0 then
11 :new.note := :old.note * 0.9;
12 l_info := 'the note can not be less than 10%';
13 elsif :new.note < 0 then
14 :new.note := 0;
15 l_info := 'the note can not be under 0';
16 end if;
17 -- Let's hope that COMMIT will save the updated value
18 select note into l_note from inscription;
19 dbms_output.put_line('Note before commit = ' || l_note);
20 commit;
21 select note into l_note from inscription;
22 dbms_output.put_line('Note after commit = ' || l_note);
23
24 -- Raise the "error", hoping that it'll just display a message,
25 -- but updated value will remain "as is"
26 raise_application_error(-20001, l_info);
27 end;
28 /
Trigger created.
SQL> set serveroutput on
SQL> update inscription set note = -1;
Note before commit = 2
Note after commit = 2
update inscription set note = -1
*
ERROR at line 1:
ORA-20001: the note can not be under 0
ORA-06512: at "SCOTT.C3_UPDATE", line 22
ORA-04088: error during execution of trigger 'SCOTT.C3_UPDATE'
SQL> select * from inscription;
NOTE
----------
2
See? Instead of 0, note keeps its original value (2). It never even got value 0 because trigger didn't end successfully. How to check that? By commenting the RAISE command:
SQL> create or replace trigger c3_update
2 before update of note on inscription
3 for each row
4 when (new.note < old.note * 0.9)
5 declare
6 pragma autonomous_transaction;
7 l_note inscription.note%type;
8 l_info varchar2(100);
9 begin
10 if :new.note > 0 then
11 :new.note := :old.note * 0.9;
12 l_info := 'the note can not be less than 10%';
13 elsif :new.note < 0 then
14 :new.note := 0;
15 l_info := 'the note can not be under 0';
16 end if;
17 -- Let's hope that COMMIT will save the updated value
18 select note into l_note from inscription;
19 dbms_output.put_line('Note before commit = ' || l_note);
20 commit;
21 select note into l_note from inscription;
22 dbms_output.put_line('Note after commit = ' || l_note);
23
24 -- Raise the "error", hoping that it'll just display a message,
25 -- but updated value will remain "as is"
26 --raise_application_error(-20001, l_info);
27 end;
28 /
Trigger created.
SQL> set serveroutput on
SQL> update inscription set note = -1;
Note before commit = 2
Note after commit = 2
1 row updated.
SQL> select * from inscription;
NOTE
----------
0
It does the job, but - the message can't be displayed (I already told you why).
If you meant to include UPDATE inscription set note = its_new_value into the trigger itself, don't - you'll get a deadlock as the initial update fires a trigger which performs update which calls a trigger which performs update etc.
Therefore, as far as I can tell, you can't do what you want, not with a trigger.
I want to know if there is any way in oracle to redirect the control from exception block to the current begin/end block.
Following is the code snippet. i variable loops through the table data sets, I want to update table i; if there is any exception go to the exec1 exception block,do the stuff and then try updating table i again.
If control goes to the exec2 exception then continue to the next iteration.
I want help as after performing steps in exec1 exception, how can I try updating table(i) again; so that if it fails again it is handled in exec1 exception block.
FOR i in c_tables
LOOP
BEGIN
---label---
Update (i);
DBMS_OUTPUT.PUT_LINE ('ROWS UPDATED IS: ' || SQL%ROWCOUNT);
EXCEPTION
WHEN exec1 THEN
BEGIN
do_stuff();
goto label;
EXCEPTION WHEN exec2 THEN
do_stuff();
continue;
END;
END;
END LOOP;
I tried to simulate what you have, in Scott's schema.
It seems that you'd be good if you moved the label in front of the BEGIN, not after it. Have a look:
SQL> DECLARE
2 l_deptno_dflt NUMBER := 50;
3 BEGIN
4 FOR i IN (SELECT distinct deptno FROM emp)
5 LOOP
6 <<this_is_label>> -- put label here ...
7 BEGIN
8 -- <<this_is_label>> -- ... not here
9 UPDATE emp
10 SET deptno = l_deptno_dflt
11 WHERE deptno = i.deptno;
12
13 DBMS_OUTPUT.put_line ('rows updated is ' || SQL%ROWCOUNT);
14 EXCEPTION
15 WHEN OTHERS
16 THEN
17 DBMS_OUTPUT.put_line ('this is exec1');
18
19 BEGIN
20 l_deptno_dflt := 40;
21 goto this_is_label;
22 END;
23 END;
24 END LOOP;
25 END;
26 /
this is exec1
rows updated is 6
rows updated is 5
rows updated is 3
PL/SQL procedure successfully completed.
SQL>
If it is where you initially put it, it raises the mentioned PLS-00375 error.
i am using this loop in my procedure and taking the email ids in i but when i am trying to run i am getting no data found error, so i want to check the values storing in i
code used:
for i in ( select EMAIL
into l_user_mail
from employee A CONNECT BY PRIOR lower(EMAIL) = lower(MANAGER_EMAIL)
START WITH lower(GUID) in (select replace(lower(group_name),'_org_slack')
from dynamic_group where id = '81')
) loop
l_user_mail:=l_user_mail || i.EMAIL;
end loop;
how to check the return value of i in sql command prompt.
i want to see the values getting in i
i want to see the values getting in i
Your FOR LOOP syntax is not correct. INTO clause should not be used in For Loop. See below how you can do that.
FOR I IN
(SELECT EMAIL
FROM EMPLOYEE A
CONNECT BY PRIOR LOWER (EMAIL) = LOWER (MANAGER_EMAIL)
START WITH LOWER (GUID) IN (
SELECT REPLACE (LOWER (GROUP_NAME), '_org_slack')
FROM DYNAMIC_GROUP
WHERE ID = '81') )
LOOP
-- l_user_mail:=:P60_IDP_GROUPS;
L_USER_MAIL := L_USER_MAIL || I.EMAIL;
-- To display value of I
DBMS_OUTPUT.PUT_LINE (I.EMAIL);
END LOOP;
Demo:
SQL> DECLARE
2 L_USER_MAIL VARCHAR2 (10);
3 BEGIN
4 FOR I IN (SELECT LEVEL
5 FROM DUAL
6 CONNECT BY LEVEL < 10)
7 LOOP
8 -- l_user_mail:=:P60_IDP_GROUPS;
9 L_USER_MAIL := L_USER_MAIL || I.level;
10 DBMS_OUTPUT.PUT_LINE (I.level);
11 END LOOP;
12 END;
13 /
1
2
3
4
5
6
7
8
9
PL/SQL procedure successfully completed.
I want insert a cursor into my custom tableObject, but it is not always found.
My RECORD:
create or replace type "RECORDRANKING" as object
(
-- Attributes
COL1 NUMBER,
COL2 VARCHAR(50),
COL3 NUMBER
-- Member functions and procedures
-- member procedure <ProcedureName>(<Parameter> <Datatype>)
)
This is object table:
CREATE OR REPLACE TYPE "TBRANKING" AS TABLE OF RECORDRANKING;
Now I go into creating a function (into a package):
CREATE OR REPLACE PACKAGE BODY PKLBOTTONI as
FUNCTION testlb
(
p_gapup IN NUMBER,
p_gadown IN NUMBER
)
RETURN SYS_REFCURSOR IS
cursor_ranking SYS_REFCURSOR;
position NUMBER ;
gap_ranking TBRANKING;
gaprecord RECORDRANKING;
upgap NUMBER;
downgap NUMBER;
BEGIN
select * into cursor_ranking from(
select pkranking.getRanking( 100 ) from dual);
LOOP
FETCH cursor_ranking INTO gap_ranking;
EXIT WHEN cursor_ranking%NOTFOUND;
INSERT INTO gap_ranking (COL1,COL2,COL3)
VALUES
(cursor_ranking.C1,
cursor_ranking.C2,
cursor_ranking.C3);
END LOOP;
return gap_ranking;
END;
END PKLBOTTONI;
I always get:
Compilation errors for PACKAGE BODY PKLBOTTONI
Error: PL/SQL: ORA-00942: table or view does not exist
Line: 32
Text: INSERT INTO gap_ranking
In the loop you're both fetching into "gap_ranking" and then trying to "insert into" it again. Insert into a collection is not a valid syntax. Fetch is ok, either in a loop or using bulk collect to fetch multiple records at once.
From your excerpt it doesn't look like you have a physical database table, so the way to do it in PL/SQL would be something like below.
For more help using collections you can check Oracle docs below too:
http://docs.oracle.com/cd/B28359_01/appdev.111/b28370/collections.htm
SQL> set serveroutput on
SQL> declare
2 gap_ranking tbranking := tbranking(null); -- initialize
3 begin
4 gap_ranking.delete; -- clear empty record
5 for cur in
6 (select level as i from dual connect by level <= 5)
7 loop
8 -- insert empty
9 gap_ranking.extend;
10 -- attribute values
11 gap_ranking(gap_ranking.last) := recordranking(1000 + cur.i, 'TEST' || cur.i, 10 + cur.i);
12 end loop;
13 -- loop to print - just to illustrate
14 for j in gap_ranking.first .. gap_ranking.last
15 loop
16 dbms_output.put_line(gap_ranking(j).col1 || ',' ||
17 gap_ranking(j).col2 || ',' ||
18 gap_ranking(j).col3);
19
20 end loop;
21 -- same as...
22 for j in 1 .. gap_ranking.count
23 loop
24 dbms_output.put_line(gap_ranking(j).col1 || ',' ||
25 gap_ranking(j).col2 || ',' ||
26 gap_ranking(j).col3);
27
28 end loop;
29 end;
30 /
1001,TEST1,11
1002,TEST2,12
1003,TEST3,13
1004,TEST4,14
1005,TEST5,15
1001,TEST1,11
1002,TEST2,12
1003,TEST3,13
1004,TEST4,14
1005,TEST5,15
PL/SQL procedure successfully completed
SQL>