I want to execute an pl/sql script via terminal but I can't manage to get it to work. It first checks whether an user exists and if he does then it copies data from some table of that user.
Problem arises when there is no user - script doesn't work because it says that table or view does not exist, and that means it somehow precompiles it, while I want it to execute line by line.
Here it is:
DECLARE
v_count INTEGER := 0;
BEGIN
SELECT COUNT (1) INTO v_count FROM SYS.DBA_USERS WHERE username = UPPER ('B');
if v_count = 0 then
DBMS_OUTPUT.put_line ('Fail');
else
insert into A.some_table (some_column)
select some_column from B.some_table
where some_column = "x";
end if;
END;
/
it throws error that table does not exist at line select some_column from B.some_table because while it indeed does not exist (the user does not) the script wouldn't actually go there.
You need to use dynamic PL/SQL for the insert, so that it is not validated at compile time but only at runtime:
DECLARE
v_count INTEGER := 0;
BEGIN
SELECT COUNT (1) INTO v_count FROM SYS.DBA_USERS WHERE username = UPPER ('B');
if v_count = 0 then
DBMS_OUTPUT.put_line ('Fail');
else
EXECUTE IMMEDIATE
'insert into A.some_table (some_column)
select some_column from B.some_table
where some_column = ''x''';
end if;
END;
/
Related
My task is to insert entries from one table to another by using parametrized cursor. Here's what I have tried
DECLARE
oldroll NUMBER;
newroll NUMBER;
oldname VARCHAR2(25);
newname VARCHAR2(25);
CURSOR c_orollcall (roll_no NUMBER,name VARCHAR2) IS SELECT * FROM o_rollcall;
PROCEDURE procedure_2;
PROCEDURE procedure_2 AS
BEGIN
OPEN c_orollcall;
LOOP
FETCH c_orollcall INTO oldroll ,oldname;
SET #count = 0;
SELECT roll_no INTO #count FROM n_rollcall WHERE EXISTS (oldroll);
IF #count>0 THEN
DBMS_OUTPUT.PUT_LINE('ENTRY ALREADY EXISTS');
ELSE
INSERT INTO n_rollcall VALUES (oldroll,oldname);
END IF;
EXIT WHEN c_orollcall%NOTFOUND;
END LOOP;
CLOSE c_orollcall;
END;
/
BEGIN
procedure_2;
END
/
I am getting a bunch of errors and dont' understand how to proceed further.I previously posted a question about this too but it generated more errors .
here's the problem statement:
Write a PL/SQL block of code using parameterized Cursor that will merge the data available in
the newly created table N_RollCall with the data available in the table O_RollCall. If the data in
the first table already exist in the second table then that data should be skipped.
I would suggest you to use an extra variable to store the result and check it in IF codition
SET count = 0;
SELECT COUNT(roll_no) INTO count FROM n_rollcall WHERE EXISTS (oldroll);
Updated code:
CREATE OR REPLACE PROCEDURE procedure_2 AS
DECLARE count INT default 0;
BEGIN
OPEN c_orollcall;
LOOP
FETCH c_orollcall INTO oldroll ,oldname;
SET count = 0;
SELECT roll_no INTO count FROM n_rollcall WHERE EXISTS (oldroll);
IF count>0 THEN
DBMS_OUTPUT.PUT_LINE('ENTRY ALREADY EXISTS');
ELSE
INSERT INTO n_rollcall VALUES (oldroll,oldname);
END IF;
EXIT WHEN c_orollcall%NOTFOUND;
END LOOP;
CLOSE c_orollcall;
END;
/
I'm new with oracle sql. I am trying to make a trigger that counts when X user performs an update or an insert, but the TRANSACTIONCONTROL table shows it like this:
DATE--------- USER-----------INSERT----UPDATE
10/03/2022 UserParcial 1 0
10/03/2022 UserParcial 0 1
10/03/2022 UserParcial 1 0
But I want it to look like this:
DATE--------- USER-----------INSERT----UPDATE
10/03/2022 UserParcial 2 1
This is my trigger:
create or replace NONEDITIONABLE TRIGGER TRANSACTIONCONTROL_Trig
AFTER INSERT OR DELETE OR UPDATE on products
for each row
DECLARE
dataTran date;
userTran varchar(30);
InsertTran number:=0;
UpdateTran number:=0;
BEGIN
SELECT SYSDATE INTO dateTran FROM DUAL;
SELECT USER INTO userTran FROM DUAL;
IF INSERTING THEN
InsertTran := InsertTran +1;
INSERT INTO TransactionControl(date, user, insert, updates)
VALUES(dateTran, userTran, insertTran, updateTran);
END IF;
IF UPDATING THEN
updateTran:= updateTran+1;
INSERT INTO TransactionControl(date, user, insert, updates)
VALUES(dateTran, userTran, insertTran, updateTran);
END IF;
END;
If you don't need exact numbers, than mining ALL_TAB_MODIFICATIONS periodically could probably suffice. (I'm curious as to what business function having the count provides)
But if you really must use a trigger, then a compound trigger lets you keep counts at row level, but then summarise at statement level.
Some pseudo code below
create or replace trigger mytrig
for insert or update on mytable
compound trigger
ins_cnt int;
upd_cnt int;
before statement is
begin
ins_cnt := 0;
upd_cnt := 0;
end before statement;
after each row is
begin
if inserting then ins_cnt := ins_cnt + 1; end if;
if updating then upd_cnt := upd_cnt + 1; end if;
end after each row;
after statement is
begin
insert into txn_control ( ... ) values (ins_cnt, upd_cnt);
end after statement;
end;
/
I have this stored proc :
begin
Sp_Racf_Test;
end;
I don't want to edit anything inside this proc.
I want to run a loop statement where it should run the proc Sp_Racf_Test until
a column RACF_ID in table tem_joins is null. I mean the loop of running stored procedure should stop when there are no null values in the column RACF_ID of table tem_joins.
Please suggest a query
Try the below one:
declare
v_count number := 1;
begin
while v_count > 0 loop
Select count(*) into v_count from tem_joins
where racf_id is null;
if v_count > 0 then
Sp_Racf_Test;
else
exit;
end if;
end loop;
end;
I wrote this query to delete one user from different tables using pl/sql.
Example: I run this query to delete one user:
user SPIKETJ, code 01234, code_id 85412974 and l_code_user SPIKETJ
declare
l_code_name table_2.cod_name%type;
l_code table_2.cod_emp%type;
l_code_id table_0.cod_id%type;
l_code_user table_03.cod_user%type;
begin
l_code_name := 'SPIKETJ';
l_code := '01234';
l_code_id := '85412974';
l_code_user := 'SPIKETJ';
DELETE table_2 WHERE cod_emp IN (l_code);
commit;
DELETE table_65 WHERE cod_emp IN (l_code);
commit;
DELETE table_41 WHERE cod_name IN (l_code_name);
commit;
DELETE table_18 WHERE cod_name IN (l_code_name);
commit;
DELETE table08 WHERE cod_user IN (l_code_name);
commit;
DELETE table_0 WHERE cod_docum IN (l_code_id);
commit;
DELETE table_17 WHERE cod_id IN (l_code_id);
commit;
DELETE table_03 WHERE cod_user IN (l_code_user);
commit;
END;
When I have to delete one user I only change/assign values for:
l_code_name, l_code, l_code_id, l_code_user.
But now, I have to delete almost 20 users!
So I wanna know if I have to run this query 20 times changing the variable values each time ?
OR
Can I write a query/block where running one time deletes the 20 users I wish to?
You can Create procedure as suggested above by Tony Andrews.
Procedure
Create or replace Procedure Delete_user
(l_code_name IN your_users.cod_name%type, -- Declare your IN parameters here
l_code IN your_users.cod_emp%type,
l_code_id IN your_users.cod_id%type,
l_code_user IN your_users.cod_user%type
)
AS
-- Declare your local variables
v_code_name your_users.cod_name%type := l_code_name;
v_code your_users.cod_emp%type := l_code;
v_code_id your_users.cod_id%type := l_code_id ;
v_code_user your_users.cod_user%type := l_code_user;
BEGIN
--- write your code(delete statements)
DELETE from your_users
WHERE cod_emp IN (v_code);
commit;
dbms_output.put_line( 'USER : ' || ' ' || v_code_user || ' is deleted.' );
---
--- similarly other delete statements
END DELETE_USER;
Output:
Procedure created.
check for errors using below command :
Show errors;
no errors
Call your Procedure for deleting users :
BEGIN
DELETE_USER('SPIKETJ',01234,85412974,'SPIKETJ');
DELETE_USER('JACKET',99999,111111,'JACKET');
--similary add other user details in order of the parameters declared in proc
END;
OUTPUT :
USER : SPIKETJ is deleted.
USER : JACKET is deleted.
Statement processed.
Read more here Procedures
For deleting users instead of calling procedure N (20) times.
Create or replace Procedure Delete_user
AS
v_code_id your_users.cod_id%type;
v_code_user your_users.cod_user%type ;
cursor C_users is select cod_id,cod_user from your_users
where 1=1; -- add condition to select users you wish to delete
BEGIN
OPEN C_users;
loop
Fetch C_users into v_code_id,v_code_user;
exit when C_users%NOTFOUND;
DELETE from your_users WHERE cod_emp IN (v_code_id); -- use your primary key
--- write delete statements for other tables
dbms_output.put_line( 'USER : ' || ' ' || v_code_user || ' is deleted.' );
End Loop;
commit;
Close C_users ;
END DELETE_USER;
-- Procedure created.
Output:
USER : mahi is deleted.
USER : xyz is deleted.
Statement processed.
I'm trying to catch two different exceptions from the same statement in PL/SQL.
If one exception is raised then it needs to keep on looping
If the other one is raised then it needs to exit the loop
And if the insert is successful it needs to exit the loop.
Here is the code I use:
create or replace procedure NewCouponGen
IS
v_min number(10) := 1000;
v_max number(10) := 99999;
v_winkel_id varchar2(200);
v_suc number(1,0);
v_new_code number(10);
CURSOR c_winkel IS
SELECT id
FROM WINKEl;
BEGIN
OPEN c_winkel;
LOOP
FETCH c_winkel INTO v_winkel_id;
v_suc := 0;
WHILE v_suc = 0
LOOP
select floor(dbms_random.value(v_min,v_max)) num INTO v_new_code from dual;
INSERT INTO WINKEL_COUPON (WINKEL_ID, COUPON_ID) VALUES (v_winkel_id, v_new_code);
-- CATCH UNQUE EXEPTION
--IF v_winkel_id != UNIQUE THEN v_suc = 1
--IF v_new_code != UNIQUE THEN KEEP ON LOOPING
--IF INSERT IS SUCCES THEN v_suc = 1
END LOOP;
EXIT WHEN c_winkel%notfound;
END LOOP;
CLOSE c_winkel;
END NewCouponGen;
The simplest thing is not to hit the first exception at all. There is a hint to ignore the duplicate violation, but that would apply to both unique constraints, so it isn't useful here. You could query to see if there is already a record with the WINKEL_ID and only insert if there is not; or as a single statement you could use a merge:
create or replace procedure NewCouponGen
IS
v_min number(10) := 1000;
v_max number(10) := 99999;
v_winkel_id varchar2(200);
v_new_code number(10);
CURSOR c_winkel IS
SELECT id
FROM WINKEl;
BEGIN
OPEN c_winkel;
LOOP
FETCH c_winkel INTO v_winkel_id;
EXIT WHEN c_winkel%notfound;
LOOP
BEGIN
v_new_code := floor(dbms_random.value(v_min,v_max));
MERGE INTO WINKEL_COUPON TGT
USING (SELECT v_winkel_id AS WINKEL_ID, v_new_code AS COUPON_ID FROM DUAL) SRC
ON (TGT.WINKEL_ID = SRC.WINKEL_ID)
WHEN NOT MATCHED THEN
INSERT (TGT.WINKEL_ID, TGT.COUPON_ID) VALUES (SRC.WINKEL_ID, SRC.COUPON_ID);
EXCEPTION
WHEN dup_val_on_index THEN
CONTINUE; -- duplicate coupon ID
END;
EXIT; -- merge was skipped because winkel ID exists, or was successful
END LOOP;
END LOOP;
CLOSE c_winkel;
END NewCouponGen;
/
The merge will only try to insert if it didn't see that a record already existed for the WINKEL_ID, so you won't get a unique constraint violation from that column. If you do get one from the COUPON_ID constraint then the exception handler on that inner block enclosing the merge - which exists only allow the exception to be caught - will send you around the loop again.
I've also taken out the v_suc flag completely; and moved the exit when clause to straight after the fetch - otherwise you will always try to insert two values for the last ID from the cursor; and taken out the context switch from the select .. from dual since you can just assign that random value directly to the variable.
You don't really need that v_new_code variable either, you can get the value in the merge instead:
MERGE INTO WINKEL_COUPON TGT
USING (SELECT v_winkel_id AS WINKEL_ID,
floor(dbms_random.value(v_min,v_max)) AS COUPON_ID FROM DUAL) SRC
ON (TGT.WINKEL_ID = SRC.WINKEL_ID)
WHEN NOT MATCHED THEN
INSERT (TGT.WINKEL_ID, TGT.COUPON_ID) VALUES (SRC.WINKEL_ID, SRC.COUPON_ID);
You can use CONTINUE to keep on looping and EXIT WHEN to exit the loop
LOOP -- After CONTINUE statement, control resumes here
select floor(dbms_random.value(v_min,v_max)) num INTO v_new_code from dual;
IF (v_new_code =='keep on looping condition') THEN
CONTINUE; --stops current loop and goes on to next iteration
END IF;
INSERT INTO WINKEL_COUPON (WINKEL_ID, COUPON_ID) VALUES (v_winkel_id, v_new_code);
EXIT WHEN v_suc==1; --or other exit conditions
END LOOP;