Rewriting SQL statement into PL/SQL - oracle

I am trying to rewrite a SQL statement in PL/SQL but I am new to this and I can't get it right.
This is the original statement:
select f.flugnummer, p.nachname, pl.sitzplatznummer, l.bezeichnung, r.reisepassnr,
sum(g.gewicht) as Luggage
from passagierliste pl join flug f on f.flugID = pl.flugID
join gepaeck g on pl.personID = g.personID
join person p on pl.personID = p.personID
join reisepass r on p.personID = r.personID
join land l on r.landID = l.landID
where f.flugnummer ='AF3012'and pl.sitzplatznummer = '13'
group by f.flugnummer, p.nachname, pl.sitzplatznummer, l.bezeichnung, r.reisepassnr;
And this is my attempt in PL/SQL where I am trying to return a passenger who is on the flight AF3012 and seat 13:
CREATE OR REPLACE PROCEDURE getPassenger IS
v_flugnummer varchar2(30);
v_sitzplatz varchar2(30);
BEGIN
v_flugnummer := 'AF3012';
v_sitzplatz := '13';
select f.flugnummer, p.nachname into v_nachname, pl.sitzplatznummer, l.bezeichnung, r.reisepassnr into v_reisepassnr,
sum(g.gewicht) as Luggage into v_luggage
from passagierliste pl join flug f on f.flugID = pl.flugID
join gepaeck g on pl.personID = g.personID
join person p on pl.personID = p.personID
join reisepass r on p.personID = r.personID
join land l on r.landID = l.landID
where f.flugnummer =v_flugnummer and pl.sitzplatznummer = v_sitzplatz
group by f.flugnummer, p.nachname, pl.sitzplatznummer, l.bezeichnung, r.reisepassnr;
IF v_sitzplatz > 0 THEN
DBMS_OUTPUT.PUT_LINE(v_nachname || ' ' || v_flugnummer || ' ' || v_sitzplatz || ' ' || v_reisepassnr || ' ' || v_luggage);
ELSE
DBMS_OUTPUT.PUT_LINE( '0');
END IF;
COMMIT;
END;
/
exec getPassenger;

A little bit simplified (as I removed unnecessary columns your SELECT returned), this is what might work.
don't hardcode parameters; pass them into the procedure for more flexibility
every column select returns has to have its own variable to put that value into. You can't select e.g. 10 columns into e.g. 2 variables; that just won't work
if query doesn't return anything, it'll raise NO_DATA_FOUND you should handle
create or replace procedure getpassenger (par_flugnummer in varchar2,
par_sitzplatz in varchar2
)
is
v_nachname person.nachname%type;
begin
-- find the passenger
select p.nachname
into v_nachname
from passagierliste pl join flug f on f.flugid = pl.flugid
join gepaeck g on pl.personid = g.personid
join person p on pl.personid = p.personid
join reisepass r on p.personid = r.personid
join land l on r.landid = l.landid
where f.flugnummer = par_flugnummer
and pl.sitzplatznummer = par_sitzplatz;
-- passenger has been found - display it
dbms_output.put_line(v_nachname || ' ' || par_flugnummer || ' ' || par_sitzplatz);
exception
when no_data_found then
-- passenger hasn't been found so previous SELECT returned
-- NO_DATA_FOUND; handle it
dbms_output.put_line('Nobody sits there');
end;
/
exec getpassenger(par_flugnummer => 'AF3012', par_sitzplatz => '13');

Related

PLSQL/Oracle Nested For Loop Insert taking Long time in Oracle 12c

Following query is used in a procedure. 1st insert happening quickly but 2nd insert taking a long time . The First For Loop query returns 5000 records which is then looped through another query which returns 4000 records and then performs 2nd insert based on a function return value which is taking 15 mins to execute . All the tables used are indexed need help on optimize the query ?
declare
LV_INCT_ROLE sy_employee_master.incentive_role%type;
BEGIN
FOR ASM1 IN (SELECT a.run_id,
a.emp_no emp_no,
a.team_id own_team_id,
b.team_id team_team_id,
a.incentive_role
FROM SY_HLSIL_ASM_EMP a,
SY_DSA_ASM_TEAMWISE_HIERARCHY b
WHERE a.emp_no = b.emp_no
AND a.run_id = b.run_id
AND b.run_id = 195) LOOP
BEGIN
INSERT INTO SY_DSA_ASM_TEAMWISE
(FROM_DATE,
TO_DATE,
RUN_ID,
EMP_NO,
INCENTIVE_ROLE,
TEAM_ID,
SUM_FOR_TEAMID)
SELECT '01-jan-2021',
'31-jan-2021',
195,
ASM1.EMP_NO,
ASM1.INCENTIVE_ROLE,
ASM1.OWN_TEAM_ID,
ASM1.EMP_NO
FROM DUAL
WHERE EXISTS (SELECT 'x'
FROM SY_DSA_TRANS_HLSIL H
WHERE H.RUN_ID = 195
AND NVL(H.CALC_INCT, 'Y') = 'Y'
AND NVL(H.HDFC_REACH_FLAG, 'N') = 'N'
AND H.EMP_NO = ASM1.EMP_NO)
AND NOT EXISTS
(SELECT 'x'
FROM SY_DSA_ASM_TEAMWISE D
WHERE D.RUN_ID = 195
AND D.SUM_FOR_TEAMID = TO_CHAR(ASM1.EMP_NO));
EXCEPTION
WHEN OTHERS THEN
DBMS_OUTPUT.PUT_LINE('Ins Tm err 2: ' || SQLERRM(-SQLCODE));
raise_application_error(-20000,SQLERRM(-SQLCODE));
END;
dbms_output.put_line('1st insert done');
FOR ASM IN (SELECT ASM1.EMP_NO EMP_NO,
ASM1.INCENTIVE_ROLE INCENTIVE_ROLE,
ASM1.OWN_TEAM_ID,
-- P.TEAM_ID TEAM_TEAM_ID,
P.REPORTEE_EMP_NO REPORTEE_EMP_NO
FROM SY_DSA_ASM_POPULATE_HIERARCHY P
WHERE p.FLAG = 'LOWER'
AND P.EMP_NO = ASM1.EMP_NO
AND P.RUN_ID = ASM1.RUN_ID
AND P.RUN_ID = 195) LOOP
LV_INCT_ROLE := UPPER(package.function
(ASM.REPORTEE_EMP_NO,'31-jan-2021');
IF INSTR(package.fucntion('INCT_RL_TL_ABOV'),UPPER(TRIM(LV_INCT_ROLE))) > 0
THEN
BEGIN
INSERT INTO SY_DSA_ASM_TEAMWISE
(FROM_DATE,
TO_DATE,
RUN_ID,
EMP_NO,
INCENTIVE_ROLE,
TEAM_ID,
SUM_FOR_TEAMID)
SELECT '01-jan-2021',
'31-jan-2021',
195,
ASM.EMP_NO,
ASM.INCENTIVE_ROLE,
ASM.OWN_TEAM_ID,
ASM.REPORTEE_EMP_NO
FROM DUAL
WHERE EXISTS (SELECT 'x'
FROM SY_DSA_TRANS_HLSIL H
WHERE H.RUN_ID = 195
AND NVL(H.CALC_INCT, 'Y') = 'Y'
AND NVL(H.HDFC_REACH_FLAG, 'N') = 'N'
AND H.EMP_NO = ASM.REPORTEE_EMP_NO)
AND NOT EXISTS
(SELECT 'x'
FROM SY_DSA_ASM_TEAMWISE D
WHERE D.RUN_ID = 195
AND D.EMP_NO = ASM.EMP_NO
AND D.SUM_FOR_TEAMID =
TO_CHAR(ASM.REPORTEE_EMP_NO));
EXCEPTION
WHEN OTHERS THEN
DBMS_OUTPUT.PUT_LINE('Ins Tm err 3: ' || SQLERRM(-SQLCODE));
END;
END IF;
END LOOP;
END LOOP;
dbms_output.put_line('2nd insert done');
EXCEPTION
WHEN OTHERS THEN
DBMS_OUTPUT.PUT_LINE('Ins Tm err 4: ' || SQLERRM(-SQLCODE));
END;

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

How do i capture the update column value and use it?

Hi I have a scenario where in using a cursor loop getting all the invoice values and checking the details and updating flag values to 'E' if the update validation is satisfied and only inserting those invoices to another table which doesn't satisfy the update statement.
Is there a way by which based on the flag value we can insert those invoices?
Kindly find the Code:
Procedure
CURSOR c2
IS
SELECT *
FROM invoice_tl
WHERE process_flag = 'N';
BEGIN
FOR rec IN c2
LOOP
BEGIN
fnd_file.put_line (fnd_file.LOG, 'The Line Number is ' || ' ' || rec.line_number);
IF rec.line_number IS NOT NULL
THEN
UPDATE invoice_tl
SET process_flag = 'E',
error_description =
(SELECT 'Credit Memo line amount cannot be more than Invoice Line Amount : '
|| (rctl.extended_amount
- NVL (
(SELECT SUM (amount)
FROM ar_activity_details
WHERE customer_trx_line_id =
rctl.customer_trx_line_id),
0)
+ NVL (
(SELECT SUM (extended_amount)
FROM ra_customer_trx_lines_all
WHERE previous_customer_trx_line_id =
rctl.customer_trx_line_id),
0))
FROM ra_customer_trx_all rct,
ra_customer_trx_lines_all rctl
WHERE rct.customer_trx_id =
rctl.customer_trx_id
AND rct.org_id = 2326
AND rct.trx_number = rec.invoice_number
AND rctl.line_number = rec.line_number
AND rct.cust_trx_type_id =
ln_trans_type_id)
WHERE process_flag = 'N'
AND invoice_number = rec.invoice_number
AND line_number = rec.line_number
AND amount >
(SELECT (rctl.extended_amount
- NVL (
(SELECT SUM (amount)
FROM ar_activity_details
WHERE customer_trx_line_id =
rctl.customer_trx_line_id),
0)
+ NVL (
(SELECT SUM (extended_amount)
FROM ra_customer_trx_lines_all
WHERE previous_customer_trx_line_id =
rctl.customer_trx_line_id),
0))
FROM ra_customer_trx_all rct,
ra_customer_trx_lines_all rctl
WHERE rct.customer_trx_id =
rctl.customer_trx_id
AND rct.org_id = 2326
AND rct.trx_number =
rec.invoice_number
AND rctl.line_number =
rec.line_number
AND rct.cust_trx_type_id =
ln_trans_type_id);
fnd_file.put_line (
fnd_file.LOG,
'Error Message if the CM amount more than the Invoice Line amount.');
COMMIT;
END IF;
END;
BEGIN
fnd_file.put_line (
fnd_file.LOG,
'The Process FLag is : ' || rec.process_flag);
INSERT INTO second_table (
customer_number,
orig_system_cust_reference,
orig_system_add_reference,
customer_name,
locations,
inv_date,
creation_date,
inv_num,
balance_amount,
customer_trx_id,
customer_trx_line_id,
NAME,
term_desc,
term_id,
gl_date,
rec_segments1,
rec_segments2.....
END;
END LOOP;
END
Your best bet is to use the RETURNING INTO clause. So you'll define an array and capture the appropriate parts of the updated rows:
declare
type line_number_tt is table of invoice_tl.line_number%TYPE;
line_number_array line_number_tt;
begin
....
update invoice_tl
...
returning line_number bulk collect into line_number_array;
[do stuff with the array here]
end;
I would really try to get rid of the select-then-loop you have, if you can. That's row by row processing and there's a reason it's called "slow-by-slow."

The procedure below(when i uncomment the commented parts) shows 'no data found'..?

Hi I'm trying the code the below logic..i need help..
When i run the following procedure i get all the sids along with the corresponding pid for a particular month. but when i uncomment the parts that i have commented here i get the month and year displayed and then a message saying 'no data found'. Where am i going wrong?
create or replace PROCEDURE mas(V_MONTH NUMBER DEFAULT NULL,V_YEAR NUMBER DEFAULT NULL,V_AID VARCHAR2) AS
V_MID VARCHAR2(50);
V_SID VARCHAR2(50);
v_v_month number := nvl(V_MONTH,to_number(to_char(sysdate,'mm')));
v_v_year number := nvl(V_YEAR,to_number(to_char(sysdate,'yyyy')));
v_is_sub PM.IS_SUB%TYPE;
V_DIST_s NUMBER;
V_DIST_t NUMBER;
cursor c1
is
select distinct a.mid,
b.sid
from pmt a
inner join smt b
on (a.mid = b.mid)
where a.AID = V_AID
AND A.AID = B.AID
AND EXTRACT(MONTH FROM A.RDATE)= v_v_month AND
EXTRACT(YEAR FROM A.RDATE)= v_v_year
order by mid;
BEGIN
dbms_output.put_line('month : ' || v_v_month);
dbms_output.put_line('year : ' || v_v_year);
/*
select IS_SUB into v_is_sub from program_master where pid = 'V_AID';
IF v_is_sub = 1 then
select count(*) into V_DIST_S from (select distinct sid from smt where aid = 'v_aid');
select count(*) into V_DIST_T from (select distinct sid from tm where aid = 'v_aid');
if(V_DIST_S = V_DIST_T) then
*/
for rec1 in c1
loop
dbms_output.put_line('MID : ' || rec1.MID);
dbms_output.put_line('SID : ' || rec1.SID);
end loop;
-- else
-- dbms_output.put_line('count of sids do not match');
--end if;
--else
--dbms_output.put_line('No sids available for the mentioned pid.');
--end if;
END MAS;
where pid = 'V_AID';
V_AID is a variable, however, you have enclosed it within single-quotation marks, which makes it a string. So, you are actually looking for the value 'V_AID' and not the value of the variable.
Modify it to:
select IS_SUB into v_is_sub from program_master where pid = V_AID;
And do the same wherever you have enclosed the variable in single-quotation marks.

Return multiple values from an Oracle function

I need to return several varchar variables from an Oracle function but when it returns something the function exits immediately. Is there any way I can do this?
Here I return the first value that matches the condition but I want to return all of them.
create or replace function f_pilotosContinentes return varchar as
cursor cc(codigoPiloto int) is
select count(*) as numero from(
select distinct pi.codigo, c.nome from Voo v
join Rota r on r.codigo = v.rota
join Aeroporto a on a.codigo = r.localDestino
join Pais p on p.codigo = a.pais
join Continente c on c.codigo = p.continente
join PilotoVoo pv on pv.voo = v.codigo
join Piloto pi on pi.codigo = pv.piloto
where pi.codigo = codigoPiloto);
pilotos cc%rowtype;
cursor cc1 is
select p.codigo from Piloto p;
codPilotos cc1%rowtype;
duracao int;
temp int;
piloto varchar(20);
begin
duracao := 0;
open cc1;
loop
fetch cc1 into codPilotos;
exit when cc1%notfound;
--Verificar para cada piloto
open cc(codPilotos.codigo);
loop
fetch cc into pilotos;
exit when cc%notfound;
if pilotos.numero between 3 and 4 then
--Buscar a duração total dos voos dele
select sum(v.duracao) into temp from Voo v
join PilotoVoo pv on pv.voo = v.codigo
join Piloto p on p.codigo = pv.piloto
where p.codigo = codPilotos.codigo;
if temp > duracao or duracao = 0 then
duracao := temp;
end if;
end if;
end loop;
close cc;
end loop;
close cc1;
open cc1;
loop
fetch cc1 into codPilotos;
exit when cc1%notfound;
--Verificar para cada piloto
open cc(codPilotos.codigo);
loop
fetch cc into pilotos;
exit when cc%notfound;
if pilotos.numero >= 3 then
--Buscar a duração total dos voos dele
select sum(v.duracao) into temp from Voo v
join PilotoVoo pv on pv.voo = v.codigo
join Piloto p on p.codigo = pv.piloto
where p.codigo = codPilotos.codigo;
if temp = duracao then
select p.nome into piloto from Piloto p
where p.codigo = codPilotos.codigo;
return piloto;
end if;
end if;
end loop;
close cc;
end loop;
close cc1;
dbms_output.put_line('Nenhum piloto viajou para todos os continentes.');
return null;
end;
There are a number of ways you can return multiple strings from a PL/SQL method:
Populate and return a VARCHAR2 array instead of a single VARCHAR2
Use a procedure instead with multiple OUT parameters (suitable if there are a fixed number of return values you want to return, especially if they have different meanings - e.g. name, address, state, etc.)
Use a view instead. This would require transforming the procedural logic from your function into SQL predicates.
Side note: if the function is changed to return an array, you can then if you wish change it to a pipelined function, which would mean that if you call it from a SQL query, it will begin consuming rows as soon as the function returns them, instead of waiting for the function to finish. This may help with performance (both speed and memory usage).

Resources