How to validate and print message when there is no data? PL SQL - sql

Using PL/SQL I'm trying to validate and print a message when there's no data found on a cursor. My main problem is that if I use the %NOTFOUND it gets printed multiple times or along when data is found.
Here is my code:
set SERVEROUTPUT ON
set verify off
DECLARE
rut int;
CURSOR Ejercicio2 (rut int)
IS
SELECT alumno.nombre,alumno.apellidoP,alumno.apellidoM,Ramo.descripcion,profesor.nombre
FROM alumno
INNER JOIN alumnoramo ON alumnoramo.cod_matricula = alumno.cod_matricula
INNER JOIN Ramo ON ramo.cod_ramo = alumnoramo.cod_ramo
INNER JOIN profesor ON profesor.cod_prof = ramo.cod_prof
WHERE alumno.cod_matricula = rut;
alumno_nombre VARCHAR2(45);
alumno_apellido VARCHAR2(45);
alumno_apellidoM VARCHAR2(45);
ramo_nombre VARCHAR2(45);
profesor_nombre VARCHAR2(45);
BEGIN
rut := '&rut';
OPEN Ejercicio2 (rut);
LOOP
FETCH Ejercicio2 INTO alumno_nombre,alumno_apellido,alumno_apellidoM,ramo_nombre,profesor_nombre;
EXIT WHEN Ejercicio2%NOTFOUND;
dbms_output.put_line('Nombre: ' || alumno_nombre);
dbms_output.put_line('Apellido: ' || alumno_apellido);
dbms_output.put_line('Apellido Materno: ' || alumno_apellidoM);
dbms_output.put_line('Ramo: ' || ramo_nombre);
dbms_output.put_line('Profesor: ' || profesor_nombre);
END LOOP;
CLOSE Ejercicio2;
END;
When the user inputs a "rut" it should normally print the found data and end. If the input "rut" and no data is returned, then it should print a message and ending the program. I haven't being able to do so.

use the below code block .let me know if you get some error.I just wrote it on notepad and it should as I expect.
set SERVEROUTPUT ON
set verify off
DECLARE
rut int;
CURSOR Ejercicio2 (rut int)
IS
SELECT alumno.nombre,alumno.apellidoP,alumno.apellidoM,Ramo.descripcion,profesor.nombre
FROM alumno
INNER JOIN alumnoramo ON alumnoramo.cod_matricula = alumno.cod_matricula
INNER JOIN Ramo ON ramo.cod_ramo = alumnoramo.cod_ramo
INNER JOIN profesor ON profesor.cod_prof = ramo.cod_prof
WHERE alumno.cod_matricula = rut;
TYPE Ejercicio2_typ IS TABLE OF Ejercicio2%ROWTYPE;
Ejercicio2_tbl Ejercicio2_typ;
BEGIN
rut := '&rut';
OPEN Ejercicio2 (rut);
FETCH Ejercicio2 BULK COLLECT INTO Ejercicio2_tbl;
IF Ejercicio2_tbl.count >0 THEN
FOR rec IN Ejercicio2_tbl.first..Ejercicio2_tbl.last LOOP
dbms_output.put_line('Nombre: ' || Ejercicio2_tbl(rec).alumno_nombre);
dbms_output.put_line('Apellido: ' || Ejercicio2_tbl(rec).alumno_apellido);
dbms_output.put_line('Apellido Materno: ' || Ejercicio2_tbl(rec).alumno_apellidoM);
dbms_output.put_line('Ramo: ' || Ejercicio2_tbl(rec).ramo_nombre);
dbms_output.put_line('Profesor: ' || Ejercicio2_tbl(rec).profesor_nombre);
END LOOP;
ELSE
dbms_output.put_line('No data found');
END IF;
CLOSE Ejercicio2;
END;
/
Happy coding.
Mark it as an answer if it satisfy your needs.

Just a variation and a clean way of handling the scenario. Hope it helps.
CREATE OR REPLACE TYPE obj IS OBJECT
(
nombre VARCHAR2(100),
apellidoP VARCHAR2(100)
apellidoM VARCHAR2(100)
descripcion VARCHAR2(100)
nombre VARCHAR2(100)
);
CREATE OR REPLACE TYPE tab IS TABLE OF obj;
SET SERVEROUTPUT ON
SET verify OFF
DECLARE
rut INT:=&Enter_rut;
tab1 tab;
BEGIN
SELECT obj(alumno.nombre,alumno.apellidoP,alumno.apellidoM,Ramo.descripcion,profesor.nombre) BULK COLLECT
INTO tab1
FROM alumno
INNER JOIN alumnoramo
ON alumnoramo.cod_matricula = alumno.cod_matricula
INNER JOIN Ramo
ON ramo.cod_ramo = alumnoramo.cod_ramo
INNER JOIN profesor
ON profesor.cod_prof = ramo.cod_prof
WHERE alumno.cod_matricula = rut;
IF tab1.EXISTS(1) THEN
FOR I IN tab1.FIRST..tab1.LAST
LOOP
dbms_output.put_line('Nombre: ' || tab1(i).nombre);
dbms_output.put_line('Apellido: ' || tab1(i).apellidoP);
dbms_output.put_line('Apellido Materno: ' || tab1(i).apellidoM);
dbms_output.put_line('Ramo: ' || tab1(i).descripcion);
dbms_output.put_line('Profesor: ' || tab1(i).nombre);
END LOOP;
ELSE
dbms_output.put_line('no data found for the Input');
END IF;
END;

You could set a Boolean variable in the loop, and then check at the end whether it was true or false:
set define on
accept rut number prompt 'Introduza un identificador de class: '
var rut number
exec :rut := &rut
declare
cursor ejercicio2
( cp_rut int )
is
select alumno.nombre as alumno_nombre
, alumno.apellidop as alumno_apellido
, alumno.apellidom as alumno_apellidom
, ramo.descripcion as ramo_nombre
, profesor.nombre as profesor_nombre
from alumno
join alumnoramo
on alumnoramo.cod_matricula = alumno.cod_matricula
join ramo
on ramo.cod_ramo = alumnoramo.cod_ramo
join profesor
on profesor.cod_prof = ramo.cod_prof
where alumno.cod_matricula = cp_rut;
l_encontro boolean := false;
begin
for r in ejercicio2(:rut)
loop
l_encontro := true;
dbms_output.put_line('Nombre: ' || r.alumno_nombre);
dbms_output.put_line('Apellido: ' || r.alumno_apellido);
dbms_output.put_line('Apellido Materno: ' || r.alumno_apellidom);
dbms_output.put_line('Ramo: ' || r.ramo_nombre);
dbms_output.put_line('Profesor: ' || r.profesor_nombre);
end loop;
if not l_encontro then
dbms_output.put_line('No se encontraron las classes por rut ' || :rut);
end if;
end;
/
(Excuse my Google Spanish.)
Or you could make it a numeric value initialised to 0 and report the number of rows processed.

Related

Relation "" already exist dynamic query

I'm trying to execute a query within a loop which is within another loop, which is within an anonymous code block.
I'm trying to run this query
query := 'CREATE VIEW hopsPartialDistance AS '
||'SELECT AvgDistance '
||'FROM Distance '
||'WHERE PlanetOrigin = ' || rHops.PlanetOrigin
||' AND PlanetDestination = ' || rHops.PlanetDestination;
EXECUTE query;
But I keep getting the error
relation "hopspartialdistance" already exists
I then run
\d hopspartialdistance;
And the output is
Did not find any relation named "hopspartialdistance".
So I don't know where the error is coming from.
Here's the full function
DO
$$
DECLARE
routeDistance real := 0.0;
hopDistance real := 0.0;
rRoute record;
rHops record;
query text := '';
BEGIN
FOR rRoute IN
SELECT MonitoringKey FROM TradingRoute
LOOP
query := 'CREATE VIEW PortsOfCall AS '
||'SELECT PlanetID, VisitOrder '
||'FROM EnrichedCallsAt '
||'WHERE MonitoringKey = ' || rRoute.MonitoringKey
||' ORDER BY VisitOrder';
EXECUTE query;
CREATE VIEW Hops AS
SELECT A.PlanetID AS PlanetOrigin,
B.PLanetID AS PlanetDestination
FROM PortsOfCall A INNER JOIN PortsOfCall B ON A.VisitOrder + 1 = B.VisitOrder;
routeDistance = 0.0;
FOR rHops IN
SELECT PlanetOrigin, PlanetDestination FROM Hops
LOOP
query := 'CREATE VIEW hopsPartialDistance AS '
||'SELECT AvgDistance '
||'FROM Distance '
||'WHERE PlanetOrigin = ' || rHops.PlanetOrigin
||' AND PlanetDestination = ' || rHops.PlanetDestination;
EXECUTE query;
hopDistance = (SELECT SUM(AvgDistance) FROM hopsPartialDistance) ;
routeDistance = routeDistance + hopDistance;
END LOOP;
INSERT INTO RouteLength (RouteMonitoringKey, RouteTotalDistance)
SELECT rRoute.MonitoringKey, routeDistance;
DROP VIEW Hops
CASCADE;
DROP VIEW PortsOfCall
CASCADE;
END LOOP;
END;
$$;

How can i turn this pl/sql into a procedure

I had to write this query for an assignement. So we have a database and we are pulling information from it, this is going to work with some back end c# eventually. Is there anything i can do , knowing im going to reuse this, in order to make it better and more adaptable when the day comes when i have to connect it all.
set serveroutput on
DECLARE
LV_DATE HVK_RESERVATION.RESERVATION_START_DATE%TYPE;
LV_SERV VARCHAR(100);
CURSOR LCUR_RES IS
SELECT *
FROM HVK_RESERVATION R
INNER JOIN HVK_PET_RESERVATION PR
ON R.RESERVATION_NUMBER = PR.RES_RESERVATION_NUMBER
INNER JOIN HVK_PET P
ON P.PET_NUMBER = PR.PET_PET_NUMBER
INNER JOIN HVK_OWNER OW
ON OW.OWNER_NUMBER = P.OWN_OWNER_NUMBER
WHERE R.RESERVATION_START_DATE < LV_DATE
AND R.RESERVATION_END_DATE > LV_DATE;
CURSOR LCUR_SERVICE(PET_RES_NUM NUMBER) IS
SELECT *
FROM HVK_SERVICE S
INNER JOIN HVK_PET_RESERVATION_SERVICE PRS
ON PRS.SERV_SERVICE_NUMBER = S.SERVICE_NUMBER
AND PRS.PR_PET_RES_NUMBER = PET_RES_NUM;
BEGIN
LV_DATE := TO_DATE('&logdate', 'yy-mm-dd');
DBMS_OUTPUT.PUT_LINE('Kennel log for ' || '' || LV_DATE);
DBMS_OUTPUT.PUT_LINE('-------------------------------');
FOR I IN LCUR_RES LOOP
DBMS_OUTPUT.PUT_LINE('Run:' || '' || I.RUN_RUN_NUMBER || ' ' ||
'Pet: ' || '' || I.PET_NAME || ' ' ||
I.OWNER_LAST_NAME || ' Pet Reservation: ' || '' ||
I.PET_RES_NUMBER);
DBMS_OUTPUT.PUT_LINE('Reservation start/end ' || ' ' ||
I.RESERVATION_START_DATE || ' ' ||
I.RESERVATION_END_DATE);
DBMS_OUTPUT.PUT('Services : ');
FOR X IN LCUR_SERVICE(I.PET_RES_NUMBER) LOOP
DBMS_OUTPUT.PUT(X.SERVICE_DESCRIPTION || ' ');
END LOOP;
DBMS_OUTPUT.PUT_LINE('');
FOR LREC_LOG IN (SELECT *
FROM HVK_KENNEL_LOG KL
WHERE KL.PR_PET_RES_NUMBER = I.PET_RES_NUMBER
) LOOP
DBMS_OUTPUT.PUT_LINE('Notes: ' || '' ||
LREC_LOG.KENNEL_LOG_SEQUENCE_NUMBER || ' ' ||
'Log Note: ' || '' || LREC_LOG.KENNEL_LOG_NOTES);
END LOOP;
DBMS_OUTPUT.PUT_LINE(' ');
END LOOP;
END;
It it supposed to output the run number , reservation number , pet name , and any relate notes.
you can replace DECLARE with CREATE OR REPLACE PROCEDURE my_proc(in_logdate in date) IS.
in that case my_proc will be the name of your procedure.
you should also use a parameter instead of &logdate
so e.g. parameter name in_logdate of type date
...
LV_DATE := in_logdate;
...

Column moving in Oracle Pl/Sql with for loop

For example, I want to move my 2 Column between for example KW_01 (dropdown) and KW_03 (dropdown) to between KW_04 (dropdown) and KW_06 (dropdown). I have 53 column in a table. I wrote code (see below) but unfortunately it does not work properly. He does that when I start a code.
Unexpected results:
Outer Loop counter is kw_04 Inner Loop counter is 1
Outer Loop counter is kw_04 Inner Loop counter is 2
Outer Loop counter is kw_04 Inner Loop counter is 3
Outer Loop counter is kw_05 Inner Loop counter is 1
Outer Loop counter is kw_05 Inner Loop counter is 2
Outer Loop counter is kw_05 Inner Loop counter is 3
Outer Loop counter is kw_06 Inner Loop counter is 1
Outer Loop counter is kw_06 Inner Loop counter is 2
Outer Loop counter is kw_06 Inner Loop counter is 3
DECLARE
plsql VARCHAR2(500);
BEGIN
For i in (SELECT column_id
FROM alsi_bedarfsplanung unpivot(column_value FOR column_id IN("KW_01", "KW_02", "KW_03", "KW_04", "KW_05", "KW_06"))
WHERE column_id BETWEEN :drp1 AND :drp2
and id = 1)
LOOP
FOR o in (SELECT column_value
FROM alsi_bedarfsplanung unpivot(column_value FOR column_id IN("KW_01", "KW_02", "KW_03", "KW_04", "KW_05", "KW_06"))
WHERE column_id BETWEEN :drp3 AND :drp4
and id = 1)
LOOP
plsql := ' UPDATE ALSI_BEDARFSPLANUNG SET ' || i.column_id || ' = ' ||
o.column_value || ' where ID = 1 ';
EXECUTE IMMEDIATE plsql;
END LOOP;
END LOOP;
END;
My DB
What i want,
Outer Loop counter is kw_04 Inner Loop counter is 1//(KW_1 Value)
Outer Loop counter is kw_05 Inner Loop counter is 2//(KW_2 Value)
Outer Loop counter is kw_06 Inner Loop counter is 3//(KW_3 Value)
The problem is the way you're looping within a loop; what you actually need to do is identify the columns to update to and from separately, and then loop over each set of columns to do the updates.
Even better would be to do all the work in a single update, like so:
-- should error with "Invalid columns specified" due to final proc call,
-- but should update the two rows accordingly
DECLARE
TYPE col_array IS TABLE OF VARCHAR2(30) INDEX BY pls_INTEGER;
v_cols_to_update_arry col_array;
v_cols_update_from_arry col_array;
PROCEDURE update_cols (p_id IN INTEGER,
p_drp1 IN VARCHAR2,
p_drp2 IN VARCHAR2,
p_drp3 IN VARCHAR2,
p_drp4 IN VARCHAR2)
IS
v_sql CLOB := 'UPDATE alsi_bedarfsplanung SET '||CHR(10);
BEGIN
SELECT column_id
BULK COLLECT INTO v_cols_to_update_arry
FROM alsi_bedarfsplanung
UNPIVOT (column_val FOR column_id IN (kw_01, kw_02, kw_03, kw_04, kw_05, kw_06, kw_07, kw_08))
WHERE column_id BETWEEN p_drp1 AND p_drp2
AND id = p_id;
SELECT column_id
BULK COLLECT INTO v_cols_update_from_arry
FROM alsi_bedarfsplanung
UNPIVOT (column_val FOR column_id IN (kw_01, kw_02, kw_03, kw_04, kw_05, kw_06, kw_07, kw_08))
WHERE column_id BETWEEN p_drp3 AND p_drp4
AND id = p_id;
IF v_cols_to_update_arry.count > 0
AND v_cols_update_from_arry.count > 0
AND v_cols_to_update_arry.count = v_cols_update_from_arry.count THEN
FOR i IN 1..v_cols_to_update_arry.count
LOOP
if i = 1 then
v_sql := v_sql || ' ' || v_cols_to_update_arry(i) || ' = ' || v_cols_update_from_arry(i);
else
v_sql := v_sql || ',' || CHR(10) || ' ' || v_cols_to_update_arry(i) || ' = ' || v_cols_update_from_arry(i);
end if;
END LOOP;
v_sql := v_sql || chr(10) || 'where id = :p_id';
EXECUTE IMMEDIATE v_sql USING p_id;
ELSE
raise_application_error(-20001, 'Invalid columns specified');
END IF;
END update_cols;
BEGIN
update_cols (p_id => 1,
p_drp1 => 'KW_04',
p_drp2 => 'KW_06',
p_drp3 => 'KW_01',
p_drp4 => 'KW_03');
COMMIT;
update_cols (p_id => 2,
p_drp1 => 'KW_05',
p_drp2 => 'KW_08',
p_drp3 => 'KW_01',
p_drp4 => 'KW_04');
COMMIT;
update_cols (p_id => 1,
p_drp1 => 'KW_01',
p_drp2 => 'KW_02',
p_drp3 => 'KW_05',
p_drp4 => 'KW_05');
END;
/
This works by fetching each list of columns into a separate array, looping over the arrays to build up the list of columns being updated, before concatenating that into the update statement.
Here's a demo of it working

Select into a variable with case statement in a for loop

The foo variable should be filled with a select case within a for loop and then put out. I used a goto end_loop reference inside an exception, so the loop can continue.
Before I put the exception, I had an ORA-01403 error. Now I have an ORA-01422 error. The other selects without the case statement worked fine.
declare
foo varchar2(2000);
BEGIN
FOR rec IN (SELECT something FROM somewhere)
LOOP
BEGIN
Select case when attribute = 'something' then '1' end into foo from somewhere where some_condition;
DBMS_OUTPUT.put_line( 'Something' || foo);
EXCEPTION
WHEN NO_DATA_FOUND THEN
foo := NULL;
goto end_loop;
END;
<<end_loop>>
null;
END LOOP;
END;
Edit
More detailed
declare
opdatum varchar2(2000);
opdiagnose varchar2(2000);
d_op varchar2(2000);
some_variable varchar2(2000);
BEGIN
FOR rec IN (SELECT p.name, p.vorname, p.geburtsdatum, a.kis_id, kg.kg_id FROM kg_eintraege kg
INNER JOIN aufenthalte a
ON kg.patient_nr = a.patient_nr
and kg.fall_nr = a.fall_nr
INNER JOIN personen p
ON a.patient_nr = p.pat_nr
WHERE kg.kgtitel_nr = xxxxxxa
and a.kis_id = xxxxxxb)
LOOP
BEGIN
Select kurztext into opdatum from kg_eintraege where kontext = rec.kg_id and kgtitel_nr = xxxxxxc;
Select text into opdiagnose from kg_eintraege where kontext = rec.kg_id and kgtitel_nr = xxxxxxd;
Select text into d_op from kg_eintraege where kontext = rec.kg_id and kgtitel_nr = xxxxxxe;
Select case when kurztext = 'Something' then '1' end into some_variable from kg_eintraege where kontext = rec.kg_id and kgtitel_nr = xxxxxxf;
DBMS_OUTPUT.put_line(rec.name || ' ' || rec.vorname || ' ' || TO_CHAR(rec.geburtsdatum, 'DD.MM.YYYY')
|| ' ' || rec.kis_id || ' ' || opdatum|| ' ' || opdiagnose ||
' ' || d_op || ' ' || some_variable
);
EXCEPTION
WHEN NO_DATA_FOUND THEN
some_variable := NULL;
goto end_loop;
END;
<<end_loop>>
null;
END LOOP;
END;
Update
The case statement seems to be alright. The problem is that some rows in the select are null.
Update2
No exception needed.
Solution:
select nvl((select case when kurztext = 'Something' then '1' end from kg_eintraege where kontext = rec.kg_id and kgtitel_nr = xxxxxxf and kurztext = 'Something'), 'NOT_FOUND') into some_variable from dual
Thanks to NikNik for the help.
You problem is not the CASE but your WHERE condition witch is retourning more than 1 record or 0 record found.
For more details look here.
In the second case you can do this:
select nvl( (your_case), 'NOT_FOUND' ) into foo from dual;

Oracle SQL - Alternative for dynamic query

I have a procedure (see below) which is using dynamic query. I wanted to rewrite this procedure with out using dynamic query. How do I write the conditions below?
PROCEDURE DemoProcedure(p_firstname IN VARCHAR2,
p_lastname IN VARCHAR2,
p_phone IN VARCHAR2
o_Cursor OUT t_Cursor) IS
SQLString VARCHAR2(4000);
BEGIN
SQLString :=
'SELECT * FROM
SCHEMA.TABLENAME_a A
INNER JOIN SCHEMA.TABLENAME_b B
ON A.ID = B.ID
WHERE
A.TYPE = 1 ';
IF p_firstname IS NOT NULL THEN
SQLString := SQLString || ' and UPPER(A.FIRST_NAME) like UPPER( ''' || p_firstname || ''')';
END IF;
IF p_lastname IS NOT NULL THEN
SQLString := SQLString || ' and UPPER(A.LAST_NAME) like UPPER( ''' || p_lastname || ''')';
END IF;
IF p_phone IS NOT NULL THEN
SQLString := SQLString || ' and UPPER(A.PHONE) = ''' ||
p_phone || '''';
END IF;
SQLString := SQLString || ' order by a.id ';
OPEN o_Cursor FOR SQLString;
END DemoProcedure;
It looks like you just want
OPEN o_cursor
FOR SELECT ...
WHERE A.TYPE = 1
AND (p_firstname IS NULL or upper(a.first_name) = upper(p_firstname))
AND (p_lastname IS NULL or upper(a.last_name) = upper(p_lastname))
AND (p_phone IS NULL or upper(a.phone) = p_phone)
ORDER BY a.id
I'm not sure why you'd want to bother upper-casing a phone number-- do you have character data in a phone number?
Different way writing the same SQL as #JustinCave suggested -
OPEN o_cursor
FOR SELECT ...
WHERE A.TYPE = 1
AND upper(a.first_name) = nvl(upper(p_firstname), upper(a.first_name))
AND upper(a.last_name) = nvl(upper(p_lastname),upper(a.last_name))
AND upper(a.phone) = nvl(p_phone,upper(a.phone))
ORDER BY a.id