Errors with identifiers in PL/SQL - oracle

I am trying to run some PL/SQL code but it contains some errors about identifiers plese help me with it. The code is not running
DECLARE
a := customer.purchase%TYPE;
id := &employee.empno;
BEGIN
UPDATE employee SET salary = salary + 5000;
UPDATE employee SET bonus = bonus + 1000 WHERE empno = &id;
SAVEPOINT sumeet;
UPDATE customer SET purchase = purchase + 5000 WHERE custid = a;
SELECT SUM(purchase) INTO a;
IF (a < 11000) THEN
ROLLBACK sumeet;
END IF;
COMMIT;
END;
/

in addition to Alen's fix, instead of the ROLLBACK why don't you do :
UPDATE customer SET purchase = purchase + 5000
WHERE custid = a
AND (select sum(purchase) from customer) + 5000 < 11000;
COMMIT;

1: This is wrong:
DECLARE
a := customer.purchase%TYPE;
id := &employee.empno;
You don't put a := after the variable name, & is invalid, and employee.empno is not a valid data type. i.e.:
DECLARE
a customer.purchase%TYPE;
id employee.empno%TYPE;
2: You don't need & to refer to the id variable:
UPDATE employee SET bonus = bonus + 1000 WHERE empno = &id;
i.e.:
UPDATE employee SET bonus = bonus + 1000 WHERE empno = id;

Here's a tip: Combine your two updates on employee into one.
UPDATE employee
SET salary = salary + 5000,
bonus = bonus + case when empno = &id then 1000 else 0 end;
Also, start using meaningful variable names.

Try with this block, but first change the values (1, 2) in the declaration block.
DECLARE
a customer.purchase%TYPE := 1;
id employee.empno%TYPE := 2;
BEGIN
UPDATE employee SET salary = salary + 5000;
UPDATE employee SET bonus = bonus + 1000 WHERE empno = id;
SAVEPOINT sumeet;
UPDATE customer SET purchase = purchase + 5000 WHERE custid = a;
SELECT SUM(purchase) INTO a FROM customer;
IF (a < 11000) THEN
ROLLBACK sumeet;
END IF;
COMMIT;
END;
/

Related

How do I use a variable within another variable in an ORACLE SQL Loop?

I am attempting to use a loop in my Oracle SQL code that will change the name of the schema slightly each time the loop is run. I have tried the SQL code below, creating a NUMBER variable that counts up from 1 to 3 and then concatenating it with the schema name, but I keep receiving a long error message. The schema name should be 'x_rm_mt1_d' the first loop, 'x_rm_mt2_d' the second loop, and 'x_rm_mt3_d' on the final loop. The select statement in the code below is just a small example of what I am trying to do, I didn't think it would be pertinent to include the entire query that I am looking to use the variable for.
DECLARE
l_counter NUMBER := 0;
BEGIN
LOOP
l_counter := l_counter + 1;
IF l_counter > 3 THEN
EXIT;
END IF;
DEFINE StrSchema = 'x_rm_mt' || l_counter || '_d'
WITH aed AS (
SELECT DISTINCT SSN , STATE FROM (
--Effective Date During Quarter
SELECT *
FROM
(SELECT * FROM ADDRESS_EFF_DATE
WHERE STATE = 'VT'
AND EFF_DATE >= '01-JAN-20'
AND EFF_DATE < '01-APR-20'
AND ADDRESS_KEY = '0'
)aed
WHERE aed.EFF_DATE = (
SELECT MAX(aed2.EFF_DATE)
FROM ADDRESS_EFF_DATE aed2
WHERE aed2.CONTROL_ID = aed.CONTROL_ID
AND aed2.SSN = aed.SSN)
--Effective Date Prior to Quarter
UNION
SELECT *
FROM
(SELECT *
FROM Strschema.ADDRESS_EFF_DATE
WHERE STATE = 'VT'
AND EFF_DATE < '01-JAN-20'
AND ADDRESS_KEY = '0') aed
WHERE aed.EFF_DATE = (
SELECT MAX(aed1.EFF_DATE)
FROM Strschema.ADDRESS_EFF_DATE aed1
WHERE aed.ssn=aed1.ssn
AND aed.EFF_DATE < '01-JAN-20'
AND aed1.EFF_DATE < '01-JAN-20')) )
,
--Select all records from Employee Eff Date Where Latest Hire Date is before 4/1/20
--AND Term Date is Null or Term Date is Greater than 1/1/20 eed AS (
SELECT *
FROM
(SELECT *
FROM Strschema.EMPLOYEE_EFF_DATE eed
WHERE eed.CONTROL_ID = 'SMLMKT'
AND eed.EFF_DATE = (
SELECT MAX(eed1.EFF_DATE)
FROM Strschema.EMPLOYEE_EFF_DATE eed1
WHERE eed.SSN = eed1.SSN
AND eed1.CONTROL_ID = 'SMLMKT')
) eed3
WHERE eed3.LATEST_HIRE_DATE <= '04-APR-20'
AND (eed3.LAST_TERM_DATE > '01-JAN-20'
OR eed3.LAST_TERM_DATE is NULL) ) ,
ebe AS (
SELECT *
FROM Strschema.emp_ben_elects ebe
WHERE ebe.control_id = 'SMLMKT'
AND ebe.BENEFIT_ID = 'MEDICAL'
AND ebe.life_event_date = (
SELECT MAX(x.life_event_date)
FROM Strschema.employee_life_events x
WHERE x.ssn = ebe.ssn
AND x.control_id = 'SMLMKT'
AND ebe.p_company_id_i = x.p_company_id_i
AND x.life_event_status = 'C')
AND ebe.le_seq_no = (
SELECT MAX(x.le_seq_no)
FROM Strschema.employee_life_events x
WHERE x.ssn = ebe.ssn
AND x.control_id = 'SMLMKT'
AND ebe.p_company_id_i = x.p_company_id_i
AND x.life_event_status = 'C'
AND ebe.life_event_date = x.life_event_date)
)
SELECT cmp.NAME as "Client Name" , cssn.REAL_SSN as "Employee SSN"
--, aed.SSN as "FAKE SSN REMOVE" , eed.LAST_NAME as "Last Name" , eed.FIRST_NAME as "First Name" , aed.STATE as "Resident State" , eed.LATEST_HIRE_DATE as "Hire Date" , pi.DESCR1 as "Plan Name" , ebe.OPTION_ID as "Tier" , ebe.BENEFIT_EFF_DATE as "Coverage Start Date" , eed.LAST_TERM_DATE as "Coverage End Date"
--, eed.LAST_TERM_DATE
FROM eed INNER JOIN aed ON eed.SSN = aed.SSN
LEFT JOIN ebe ON eed.SSN = ebe.SSN
JOIN Strschema.COMP_SSN cssn ON eed.SSN = cssn.SSN
LEFT JOIN Strschema.PLAN_INFO pi ON ebe.PLAN_ID = pi.PLAN_ID AND ebe.BENEFIT_ID = pi.BENEFIT_ID
JOIN Strschema.COMPANY cmp ON eed.CURRENT_CO = cmp.COMPANY_ID
END LOOP;
END;
DECLARE the variable and then set its value in the loop:
DECLARE
TYPE employee_array IS TABLE OF EMPLOYEE%ROWTYPE;
-- Assuming the tables all have the same type.
l_StrSchema VARCHAR2(30);
t_employees employee_array;
BEGIN
FOR l_counter IN 1 .. 3 LOOP
l_StrSchema := 'x_rm_mt' || l_counter || '_d';
DBMS_OUTPUT.PUT_LINE( 'Loop ' || l_counter );
EXECUTE IMMEDIATE 'SELECT * FROM ' || l_Strschema || '.EMPLOYEE'
|| ' WHERE ROWNUM < 1000'
BULK COLLECT INTO t_employees;
FOR i IN 1 .. t_employees.COUNT LOOP
DBMS_OUTPUT.PUT_LINE( t_employees(i).name );
END LOOP;
END LOOP;
END;
/
You also need to use dynamic SQL if you are dynamically setting the table's schema and will need to collect the rows into a data structure as you are working in PL/SQL (and not in the SQL scope). You can also use a FOR loop.
db<>fiddle

PLSQL UPDATE statement in LOOP takes forever

If I execute
UPDATE person
SET CUSTOM2 = TIMESTAMP_DIFF(SYSTIMESTAMP,TO_TIMESTAMP (CUSTOM1, 'YYYY-MM-DD HH24:MI:SS.FF'))
WHERE person.id = 'p01';
it updates fine but if I execute update statement inside the loop it hangs forever
DECLARE
TYPE person_ids_t IS table of person.id%type index by PLS_INTEGER;
ids_collection person_ids_t;
CURSOR cur IS select id from person where CUSTOM1 is not null;
BEGIN
OPEN cur;
-- LOOP
FETCH cur BULK COLLECT INTO ids_collection LIMIT 10;
-- EXIT WHEN ids_collection.COUNT = 0;
FORALL idx IN 1 .. ids_collection.COUNT
UPDATE person
SET CUSTOM2 = TIMESTAMP_DIFF(SYSTIMESTAMP,TO_TIMESTAMP (CUSTOM1, 'YYYY-MM-DD HH24:MI:SS.FF'))
WHERE person.id = ids_collection(idx);
COMMIT;
-- END LOOP;
CLOSE cur;
END;
When I execute above block, ScriptRunner hangs forever and I have to force quit.
Not sure why.
even changing the script to
set SERVEROUTPUT ON;
DECLARE
c_id person.id%type;
CURSOR cur IS select id from person where CUSTOM1 is not nul;
vcount integer :=0;
BEGIN
OPEN cur;
LOOP
FETCH cur into c_id;
EXIT WHEN cur%notfound;
-- UPDATE person
-- SET CUSTOM2 = TIMESTAMP_DIFF(SYSTIMESTAMP,TO_TIMESTAMP (CUSTOM1, 'YYYY-MM-DD HH24:MI:SS.FF'))
-- WHERE person.id = c_id;
dbms_output.put_line('c_id: ' || c_id);
vcount := vcount + 1;
IF vcount = 1 THEN
EXIT;
END IF;
END LOOP;
CLOSE cur;
dbms_output.put_line('vcount: ' || vcount);
END;
/
If I execute this it prints
c_id: p01
vcount: 1
but when I uncomment the update statement it hangs forever
My guess is that previous update wasn't committed and it keeps those rows locked so you're just waiting for commit (or rollback).
Anyway: why would you use an option which is suboptimal? As you've already seen, an ordinary UPDATE does the job just fine. Doing it in a loop is updating row-by-row which promises to be slow-by-slow. If I were you, I wouldn't bother.
If you want to find who's blocking who, there are Oracle dictionary views you may query. There are also numerous scripts available on the Internet, have a look. Or, if you use some GUI - like TOAD, which offers the "Schema Browser" that lets you easily see that info - use it.
Meanwhile, just for example, there's the TEST table in Scott's schema; I updated it in one session as:
SQL> update test set sal = 2000 where empno = 7369;
1 row updated.
SQL>
Then I connected to another session (also as Scott) and ran
SQL> update test set sal = 3000;
and - nothing happened. It hangs. So I connected as SYS (which has access to all dictionary views; if you have some other user with appropriate privileges, use it) and queried the database as:
SQL> select
2 (select username from v$session where sid = a.sid) blocker,
3 --
4 a.sid,
5 ' is blocking ',
6 --
7 (select username from v$session where sid = b.sid) blockee,
8 --
9 b.sid
10 from v$lock a join v$lock b on a.id1 = b.id1 and a.id2 = b.id2
11 where a.block = 1
12 and b.request > 0;
BLOCKER SID 'ISBLOCKING' BLOCKEE SID
--------------- ---------- ------------- --------------- ----------
SCOTT 141 is blocking SCOTT 92
SQL>
So, yes - my session 141 is blocking my another session 92. I should commit (or rollback) in session 141 to let session 92 proceed.

23/3 PLS-00201: identifier 'DBMS_OUPUT.PUT_LINE' must be declared

I am not sure how to resolve the above error I am getting. How should I declare it? I created the procedure below to allow a passenger to buy a metro card and add it to an existing account. Input includes account id, initial balance
SET SERVEROUTPUT ON
-- procedure to buy new metro, output error if account already exists.
CREATE OR REPLACE PROCEDURE NewCard (
input_Account_ID IN varchar,
input_Balance IN varchar,
input_age IN varchar
)
IS
acct varchar(255);
-- compare the input account id to see if it exists in the table
CURSOR S1 is SELECT Account_id from Rider_Account where ACCOUNT_ID = input_Account_ID;
BEGIN
--THIS MEANS THE RECORD EXISTS
open S1;
LOOP
FETCH S1 into acct;
EXIT WHEN S1%notfound;
END LOOP;
-- go through the records
IF (acct = input_Account_ID) THEN
Dbms_ouput.Put_line('Account exists');
ELSE
BEGIN
INSERT into Metro_Card(Card_ID, Account_ID, Balance) VALUES(Card_Sequence.NEXTVAL,input_Account_ID, input_Balance);
INSERT into rider_account(Age) VALUES (input_age);
END;
END IF;
IF (input_age <= 12) THEN
UPDATE Metro_Card
SET Discount_type = 2
Where Metro_Card.Account_ID = input_Account_ID;
ELSIF (input_age >= 65) THEN
UPDATE Metro_Card
SET Discount_type = 3
Where Metro_Card.Account_ID = input_Account_ID;
ELSE
UPDATE Metro_Card
SET Discount_type = 1
Where Metro_Card.Account_ID = input_Account_ID;
END IF;
END;
It is not
Dbms_ouput.Put_line('Account exists');
but
Dbms_ouTput.Put_line('Account exists');
-
^
missing "T"

PLSQL fetch does not exit

Have this employee table:
And a plsql query:
DECLARE
totaal_salaris INT := 0;
CURSOR medewerker_cur IS
SELECT naam, maandsal
FROM MEDEWERKERS;
medewerker_row medewerker_cur%rowtype;
BEGIN
OPEN medewerker_cur;
LOOP
FETCH medewerker_cur INTO medewerker_row;
EXIT WHEN medewerker_cur%NOTFOUND OR totaal_salaris > 50000;
UPDATE medewerkers
SET maandsal = (medewerker_row.maandsal * 1.10)
WHERE naam = medewerker_row.naam;
totaal_salaris := totaal_salaris + medewerker_row.maandsal;
END LOOP;
dbms_output.put_line('Totaal uit te keren salaris: ' || totaal_salaris);
END;
/
The intention was to increase all existing employees salary (maandsal) by 10% so long as the total salary < 50.000. But this is not working out at all..
I 3 double checked witch running the script and this is the result I get:
Some test results:
First time running:
PL/SQL procedure successfully completed.Totaal uit te keren salaris: 55000
maandsal changed into 5500;
Second time
PL/SQL procedure successfully completed.Totaal uit te keren salaris: 55000
maandsal changed into 6050
Thirth time
PL/SQL procedure successfully completed. Totaal uit te keren salaris: 54450
maandsal changed into 6655
This should be the solution.
DECLARE
total_salaris INT := 0;
CURSOR employee_cur IS
SELECT employee_id, first_name, salary
FROM employees;
employee_rec employee_cur%rowtype;
BEGIN
OPEN employee_cur;
LOOP
FETCH employee_cur INTO employee_rec;
total_salaris := total_salaris + (employee_rec.salary * 1.1);
EXIT WHEN employee_cur%NOTFOUND OR total_salaris > 50000;
UPDATE employees
SET salary = (employee_rec.salary * 1.10)
WHERE employee_id = employee_rec.employee_id;
END LOOP;
CLOSE employee_cur;
END;

Changing a Procedure to a View in Oracle

Not sure if this is possible, but I'm trying to see if I can convert this procedure to become a view because we've been having trouble with drives not populating the table when the procedure is run.
I'm trying to understand someone else's code and because of the cursors, I'm not even sure we can change this procedure to a view.
----------------------------------------------------------------------
--This Procedure will interface drive information on a nightly basis--
----------------------------------------------------------------------
Procedure HEMA_DRIVE_AUTO IS
v_start_date DATE := trunc(sysdate) -30;
v_end_date DATE := trunc(sysdate);
v_delete_stats_dt DATE := trunc(sysdate)-120;
v_total_registration_count NUMBER;
v_total_performed_count NUMBER;
v_total_collected_count NUMBER;
v_total_deferred_count NUMBER;
v_total_qns_count NUMBER;
v_existing_drive NUMBER;
v_existing_performed NUMBER;
v_maph_drive NUMBER;
--This Cursor will collect the initial data
cursor c_drive_info is
select dr.drive_id, dr.Start_time, dr.vehicle_id
from drives dr
--where dr.drive_id in(1605606);
where trunc(dr.start_time) between v_start_date and v_end_date;
--This Cursor will be used to decode the Donation Types
cursor c_procedure_codes is
select * from hema_donation_type_map hdt
where hdt.mobiles = 1 order by procedure_code_id;
--This Cursor will define the intentions but exclude theraputics inthe mapping
cursor c_intention is
select rsa_motivation_id,hema_intent_id from hema_intent_map
where rsa_motivation_id <> 4 order by rsa_motivation_id;
BEGIN
-- delete records older then 4 months
delete from hema_nightly h where trunc(h.drive_date) < v_delete_stats_dt;
commit;
FOR cur_drive IN c_drive_info LOOP
delete from hema_nightly where drive_id = cur_drive.drive_id;
commit;
-- Loop by motivation/intention
FOR cur_intent in c_intention LOOP
-- Loop to get the procedure code data
FOR cur_proc_code IN c_procedure_codes LOOP
v_total_registration_count := 0;
v_total_performed_count := 0;
v_total_collected_count := 0;
v_total_deferred_count := 0;
v_total_qns_count := 0;
v_maph_drive := 0;
-- get the count for all other procedures
select count(1)
into v_total_registration_count
from registration r
where r.drive_id = cur_drive.drive_id
and r.donation_type_id = cur_proc_code.donation_type_id
and r.motivation_id = cur_intent.rsa_motivation_id;
--get the deferral count
select count(unique(r.registration_id))
into v_total_deferred_count
from registration r
where r.drive_id = cur_drive.drive_id
and r.donation_type_id = cur_proc_code.donation_type_id
and r.motivation_id = cur_intent.rsa_motivation_id
and r.step_completed < 12
and exists (select rsc.registration_id
from reg_steps_completed rsc
where rsc.registration_id = r.registration_id
and rsc.collection_step_id = 99);
-- QNS count
select count(unique(r.registration_id))
into v_total_qns_count
from registration r
where r.drive_id = cur_drive.drive_id
and r.step_completed < 12
and not exists (select rsc.registration_id
from reg_steps_completed rsc
where rsc.registration_id = r.registration_id
and rsc.collection_step_id = 99)
and r.donation_type_id = cur_proc_code.donation_type_id
and r.motivation_id = cur_intent.rsa_motivation_id;
-- performed count is the difference between total registrations and total deferrals.
v_total_performed_count := v_total_registration_count -
(v_total_deferred_count +
v_total_qns_count);
-- not calulatind yield so keep count the same
v_total_collected_count := v_total_performed_count;
-- does this drive exist
select count(drive_id)
into v_existing_drive
from hema_nightly
where drive_id = cur_drive.drive_id
and procedure_id = cur_proc_code.procedure_code_id
and intent = cur_intent.hema_intent_id;
-- Is this an aph vehicle?
select count(vehicle_id)
into v_maph_drive
from vehicles
where veh_drive_type_uid = 2
and vehicle_id = cur_drive.vehicle_id;
if v_existing_drive > 0 then
update hema_nightly
set performed = performed + v_total_performed_count,
collected = collected + v_total_collected_count,
registered = registered + v_total_registration_count,
deferrals = deferrals + v_total_deferred_count,
qns = qns + v_total_qns_count,
drive_date = cur_drive.start_time,
mod_date = sysdate,
intent = cur_intent.hema_intent_id,
aph = v_maph_drive
where drive_id = cur_drive.drive_id
and procedure_id = cur_proc_code.procedure_code_id
and intent = cur_intent.hema_intent_id;
commit;
elsif v_existing_drive = 0 and v_total_registration_count > 0 then
insert into hema_nightly
(drive_id,
procedure_id,
performed,
collected,
registered,
deferrals,
qns,
drive_date,
mod_date,
intent,
aph)
values
(cur_drive.drive_id,
cur_proc_code.procedure_code_id,
v_total_performed_count,
v_total_collected_count,
v_total_registration_count,
v_total_deferred_count,
v_total_qns_count,
trunc(cur_drive.start_time),
sysdate,
cur_intent.hema_intent_id,
v_maph_drive);
commit;
end if;
v_existing_drive := 0;
end loop;
end loop;
end loop;
end hema_drive_auto;
Views don't perform DML (insert, update, delete) and they don't manage transactions with COMMIT and ROLLBACK; they only select and retrieve data.

Resources