Error while creating and executing a function - oracle

I have a table as follows:
SCREENING_ID PLAN_ID THEATRE_ID SCREENING_DATE SCREENING_START_HH24 SCREENING_START_MM60
1 1 3 01.06.2015 00:00:00 9 0
2 1 3 01.06.2015 00:00:00 11 30
3 1 3 01.06.2015 00:00:00 14 0
4 1 4 01.06.2015 00:00:00 14 0
I have to check whether a particular time slot is available or not.This function should contain the following input parameters: plan_id_p, theatre_id_p, screening_date_p, screening_start_hh24_p, screening_start_mm60_p. It should return 1 if having a time slot otherwise return 0.
My code:
CREATE OR REPLACE FUNCTION Func_is_time_available
(plan_id_p IN NUMBER,
theatre_id_p IN OUT NUMBER,
screening_date_p IN OUT DATE,
screening_start_hh24_p IN OUT NUMBER,
screening_start_mm60_p IN OUT NUMBER)
RETURN NUMBER
AS
return_val NUMBER;
CURSOR cr2 IS
SELECT plan_id,
THEATRE_ID,
SCREENING_DATE,
SCREENING_START_HH24,
SCREENING_START_MM60
FROM screening s
WHERE plan_id = plan_id_p and
theatre_id=theatre_id_p and
SCREENING_DATE=SCREENING_DATE_P and
SCREENING_START_HH24=SCREENING_START_HH24_P and
SCREENING_START_MM60=SCREENING_START_MM60_P;
BEGIN
OPEN cr2;
FETCH cr2 INTO plan_id_p,
THEATRE_ID_P,
SCREENING_DATE_P,
SCREENING_START_HH24_P,
SCREENING_START_MM60_P;
IF cr2%NOTFOUND THEN
return_val := 1;
ELSE
return_val := 0;
END IF;
CLOSE cr2;
RETURN return_val;
END;
And the execution part:
DECLARE
v_result NUMBER;
BEGIN v_result:=Func_is_time_available(plan_id_p=>1,
theatre_id_p=>3,
screening_date_p=>'1/JUN/2015',
screening_start_hh24_p=>9,
screening_start_mm60_p=>0);
END;
But this is giving error as:
ORA-06550: line 18, column 44:
PLS-00363: expression '<expression>' cannot be used as an assignment target
Can anyone explain what my mistake is here?
I am using oracle.

I have made changes to your code refer below
CREATE OR REPLACE FUNCTION Func_is_time_available
(plan_id_p IN NUMBER,
theatre_id_p IN NUMBER,
screening_date_p IN DATE,
screening_start_hh24_p IN NUMBER,
screening_start_mm60_p IN NUMBER)
RETURN NUMBER
AS
return_val NUMBER;
var1 number;
begin
SELECT count(*) into var1
FROM screening s
WHERE plan_id = plan_id_p and
theatre_id=theatre_id_p and
SCREENING_DATE=SCREENING_DATE_P and
SCREENING_START_HH24=SCREENING_START_HH24_P and
SCREENING_START_MM60=SCREENING_START_MM60_P;
if var1 >= 1 then -- count can be 1 or > 1
return_val:= 1;
else return_val:=0;
end if;
end;

Make all parameters as IN instead of IN OUT and try.

Related

Oracle hierarchical recursive query stop at cycle and continue with other package name

I am a little struggling at the moment with the following problem.
I want to know which package calls which packages. The table is not hierarchical.
Its the table user_dependencies.
The code so far:
CREATE OR REPLACE PACKAGE object_x is
type ObjectRec is record(
dName varchar2(250),
level number
);
type ObjectTemp is table of ObjectRec;
function Referenced(dname VARCHAR2, level NUMBER, maxl NUMBER) return ObjectTemp pipelined;
end;
/
CREATE OR REPLACE PACKAGE BODY object_x is
function Referenced(dname VARCHAR2, level NUMBER, maxl NUMBER) return ObjectTemp pipelined is
rData ObjectRec;
begin
if level >= maxl then
return;
end if;
if level = 1 then
rData.dName := name;
rData.Level := maxl;
pipe row(rData);
end if;
for r in (
select referenced_name
from user_dependencies
where name = upper(dname)
and type = 'PACKAGE BODY'
and referenced_type = 'PACKAGE'
and referenced_name != UPPER(dname)
and referenced_name != name
)
loop
rData.dName := LPAD(' ', 3, ' ') || r.Referenced_name;
rData.level := level+1;
pipe row(rData);
rData.Name := r.Referenced_name;
for r2 in (select * from table(Referenced(rData.Name, level + 1, maxl))) loop
rData.Name := LPAD(' ', 3, ' ') || r2.dName;
rData.Level := r2.Level;
pipe row(rData);
null;
end loop;
end loop;
RESULT:
Level Dname
---------- --------------------------------------------------------------------------------
1 PAC1
2 PAC2
2 PAC3
2 PAC4
2 PAC5
3 PAC6
3 PAC2
3 PAC7
3 PAC8
4 PAC9
4 PAC10
5 PAC6
5 PAC11
5 PAC3
5 PAC9
5 PAC12
5 PAC6
5 PAC3
5 PAC9
5 PAC4
5 PAC8
3 PAC10
4 PAC6
4 PAC11
4 PAC3
4 PAC9
4 PAC12
4 PAC4
EXPECTED RESULT:
Level Dname
---------- --------------------------------------------------------------------------------
1 PAC1
2 PAC2
2 PAC3
2 PAC4
2 PAC5
3 PAC6
3 PAC2
3 PAC7
3 PAC8
4 PAC9
4 PAC10
5 PAC6
5 PAC11
5 PAC3
5 PAC9
5 PAC12
5 PAC6
5 PAC3
5 PAC9
5 PAC4
5 PAC8
3 PAC10 LOOP!!!!
-----BREAK------
CONTINUE WITH OTHER PACKAGES………..
Thanks for any advice.
Ultimately, yes, it is possible to do what you want.
One way to do this is to use a nested table to store the chain of dependent packages that you've built up through the current chain of recursive function calls. You can use the MEMBER OF operator with this nested table to check whether a package is already in this chain (and so you've found a loop). If not, you can call your Referenced function recursively, but adding an extra item to this table for the current package.
I made these changes to your code and ended up with the following:
CREATE OR REPLACE TYPE varchar2_table AS TABLE OF VARCHAR2(250);
/
CREATE OR REPLACE PACKAGE object_x is
type ObjectRec is record(
dName varchar2(250),
dLevel number
);
type ObjectTemp is table of ObjectRec;
function Referenced(dname VARCHAR2, dlevel NUMBER, maxl NUMBER) return ObjectTemp pipelined;
function Referenced(dname VARCHAR2, dlevel NUMBER, maxl NUMBER, pkgs_so_far IN varchar2_table) return ObjectTemp pipelined;
end;
/
CREATE OR REPLACE PACKAGE BODY object_x is
function Referenced(dname VARCHAR2, dlevel NUMBER, maxl NUMBER) return ObjectTemp pipelined is
rData ObjectRec;
begin
for r2 in (select * from table(Referenced(dname, dlevel, maxl, varchar2_table(dname))))
loop
rData.dName := r2.dName;
rData.dLevel := r2.dLevel;
pipe row(rData);
end loop;
end;
function Referenced(dname VARCHAR2, dlevel NUMBER, maxl NUMBER, pkgs_so_far IN varchar2_table) return ObjectTemp pipelined is
rData ObjectRec;
new_pkgs_so_far varchar2_table;
loop_detected boolean;
begin
if dlevel >= maxl then
return;
end if;
if dlevel = 1 then
rData.dName := dname;
rData.dLevel := 1;
pipe row(rData);
end if;
for r in (
select referenced_name
from user_dependencies
where name = upper(dname)
and type = 'PACKAGE BODY'
and referenced_type = 'PACKAGE'
and referenced_name != UPPER(dname)
and referenced_name != name
)
loop
loop_detected := r.referenced_name MEMBER OF pkgs_so_far;
rData.dName := LPAD(' ', 3, ' ') || r.Referenced_name;
if loop_detected then
rData.dName := rData.dName || ' --- LOOP';
end if;
rData.dLevel := dLevel + 1;
pipe row(rData);
if not loop_detected THEN
new_pkgs_so_far := pkgs_so_far;
new_pkgs_so_far.EXTEND(1);
new_pkgs_so_far(new_pkgs_so_far.COUNT) := r.referenced_name;
for r2 in (select * from table(Referenced(r.referenced_name, dLevel + 1, maxl, new_pkgs_so_far))) loop
rData.dName := LPAD(' ', 3, ' ') || r2.dName;
rData.dLevel := r2.dLevel;
pipe row(rData);
end loop;
end if;
end loop;
end;
end;
/
I've modified your pipelined function to add the pkgs_so_far parameter, which contains the table of packages currently encountered. I've added another pipelined function with the same signature as your original function, and this just calls the other one with the table of packages containing only the given package.
In the modified function, we now use the MEMBER OF operator to check whether we've already come across the current package. If we have, we can add an extra --- LOOP marker to indicate this. If we haven't come across this package before, we add the package name to the table and continue to recursively examine its references. Note that assigning the table in the line new_pkgs_so_far := pkgs_so_far; appears to set new_pkgs_so_far to a copy of the table in pkgs_so_far, so we can mutate new_pkgs_so_far without this affecting pkgs_so_far.
However, there were also a number of other issues I cleared up with your code:
The package body appears to be missing the final two end; lines, one to end the procedure and one to end the package body itself.
Your code contains some references to the name field of your record, but it is named dName. I've tidied these all up to use dName.
Oracle didn't seem to like the name level being used, so I changed it to dLevel. (LEVEL is an Oracle keyword, it's used in hierarchical SQL.)
I'm not sure it's correct to pipe out a row with dLevel set to maxl in the case where the variable dlevel is 1, so I've changed this to 1 instead.
When calling your References function recursively, I've changed the first parameter to be r.referenced_name, as then we don't need to assign to rData.dName.
I've removed an unnecessary null; statement within the loop that iterates through a recursive call to your procedure.
I did also note that you've written LPAD(' ', 3, ' ') twice, but I'm not sure why: this is a long-winded way of writing ' ', where there are three spaces in the string.
Some of these errors may have arisen because you anonymised your code before posting it here but you didn't check whether the code you posted compiles before posting. In future, please ensure your code compiles, as otherwise anyone who wishes to help you will have to first fix your compilation errors.
I also don't have all of your packages (and I suspect in reality they aren't named PAC1, PAC2 and so on), so I created three packages PAC1 to PAC3. PAC2 and PAC3 both reference each other and PAC1, and the output I got was as follows:
SQL> COLUMN dname FORMAT a30
SQL> select * from table(object_x.referenced('PAC2', 1, 10));
DNAME DLEVEL
------------------------------ ----------
PAC2 1
PAC1 2
PAC3 2
PAC1 3
PAC2 --- LOOP 3

PLS-00103: Encountered the symbol " " when expecting one of the following: := . ( # % ; not null range default character

Why doesn't this work?
Any advice or solutions are greatly appreciated.
CREATE OR REPLACE TRIGGER OverReading
BEFORE UPDATE OR INSERT ON MeterReadings
FOR EACH ROW
DECLARE
emp_counter INTEGER;
max_meter INTEGER : = 5;
BEGIN
SELECT COUNT(EmployeeId) INTO emp_counter
FROM MeterReadings
WHERE EmployeeId = :NEW.EmployeeId;
IF : OLD.EmployeeId = NEW.EmployeeId THEN
RETURN;
ELSIF emp_counter >= max_meter THEN
raise_application_error(-20900,'Employee are limited to a maximum of 5 meters');
END IF;
END;
/
The Error that is thrown to me
Three syntax errors, all of them related to : character.
When fixed, trigger compiles:
SQL> CREATE OR REPLACE TRIGGER OverReading
2 BEFORE UPDATE OR INSERT ON MeterReadings
3 FOR EACH ROW
4 DECLARE
5 emp_counter INTEGER;
6 max_meter INTEGER := 5; --> here
7 BEGIN
8 SELECT COUNT(EmployeeId) INTO emp_counter
9 FROM MeterReadings
10 WHERE EmployeeId = :NEW.EmployeeId;
11 IF :OLD.EmployeeId = :NEW.EmployeeId THEN --> 2x here
12 RETURN;
13 ELSIF emp_counter >= max_meter THEN
14 raise_application_error(-20900,'Employee are limited to a maximum of 5 meters');
15 END IF;
16 END;
17 /
Trigger created.
SQL>
However: this code will run if you insert rows one-by-one. Otherwise, it will raise mutating table error as you're selecting from the same table you're inserting into (which is not allowed). If that bothers you, there are ways to fix it. Depending on database version you use, it could be a compound trigger or a type + package option.

Insert Into A Table With 200 Columns

We Have A Table in our Oracle DB With 200 Columns Which we have to do Insert into it and we have an C program which inserts requests in that table and it uses Pro*C to Call an stored program in oracle DB to do the job.
Currently we serialize all fields with TLV format{Tag-Length-Value} and have a function with a Varchar2 parameter(the serialized list of fields). for example:
008603701212345678901201100611111104800622222220000633333320100644444401201420200321164712
more human readable:
0086 037 012 123456789012 011 006 111111 048 006 222222 200 006 333333 201 006 444444 012 014 20200321164712
which means:
String Len:86
Field037 with len 12:123456789012
Field011 with len 6:111111
Field048 with len 6:222222
Field200 with len 6:333333
Field201 with len 6:444444
Field012 with len 14:20200321164712
each of these fields map to a field in the table, the stored program parses the huge string and fills them into a rowtype and finally inserts the row in the table.
For years it was working well, but when we get ready for more requests(thus more inserts) our DBA says we are using a lot of CPU for de-serializing the TLV. So we have to change our insert method.
I Currently created an structure in C and map all fields in it and call:
typedef struct
{
char field37[12+1];
char field11[6+1];
char field48[6+1];
char field200[6+1];
char field201[6+1];
char field12[14+1];
} NewRecord;
NewRecord newRecord;
TlvToStruct(sTlv, &newRecord);//Mapper Function
EXEC SQL INSERT INTO MY_TABLE(FIELD37, FIELD11, FIELD200, FIELD201, FIELD12) VALUES(:newRecord);
EXEC SQL COMMIT WORK RELEASE;
This approach works well for now, but my question is: Should I continue developing and add all 200 fields to this structure and use this approach or it's better to use PLSQL call(maybe develop and using another insert function) instead of this SQL Insert?
I am currently aware of PLSQL benefits, but there is some problems with it here:
1- Pro*C does not support PLSQL Records
[Oracle Document]1
2- It is not so reasonable to have a function with 200 parameters!
3- We use Types in plsql but never used them in Pro*C(actually I tried once and I failed long ago)
I tried to describe the problem here, if it is unclear please ask
Thanks
==================================================
EDIT:
Here is our old Get_Tag and Put_Tag functions:
FUNCTION GET_TAG(P_TAG_NAME IN VARCHAR2, P_TLV_STRG IN VARCHAR2, P_TAG_LEN OUT NOCOPY PLS_INTEGER, P_TAG_VALUE OUT NOCOPY VARCHAR2)
RETURN PLS_INTEGER IS
V_COUNTER_LOOP PLS_INTEGER := 1;
V_TLV_STRG VARCHAR2(4096) := SUBSTR(P_TLV_STRG, 5);
BEGIN
P_TAG_VALUE := NULL;
P_TAG_LEN := 0;
WHILE V_COUNTER_LOOP < LENGTH(V_TLV_STRG) LOOP
IF SUBSTR(V_TLV_STRG, V_COUNTER_LOOP, 3) = P_TAG_NAME THEN
P_TAG_LEN := TO_NUMBER(SUBSTR(V_TLV_STRG, V_COUNTER_LOOP + 3, 3));
P_TAG_VALUE := SUBSTR(V_TLV_STRG, V_COUNTER_LOOP + 6, P_TAG_LEN);
RETURN (DECLARATION_CST.OK);
END IF;
V_COUNTER_LOOP := V_COUNTER_LOOP + 6 + TO_NUMBER(SUBSTR(V_TLV_STRG, V_COUNTER_LOOP + 3, 3));
END LOOP;
RETURN 0;
EXCEPTION
WHEN OTHERS THEN
RETURN -1;
END GET_TAG;
===========================================================
FUNCTION PUT_TAG(P_TAG_NAME IN VARCHAR2, P_TAG_VALUE IN VARCHAR2, P_TLV_STRG IN OUT NOCOPY VARCHAR2)
RETURN PLS_INTEGER IS
V_COUNTER_LOOP PLS_INTEGER := 0;
TMP_VARCHAR VARCHAR2(4096);
BEGIN
P_TLV_STRG := SUBSTR(P_TLV_STRG, 5);
V_COUNTER_LOOP := 1;
WHILE V_COUNTER_LOOP < LENGTH(P_TLV_STRG) LOOP
IF SUBSTR(P_TLV_STRG, V_COUNTER_LOOP, 3) = SUBSTR(P_TAG_NAME, 1, 3) THEN
TMP_VARCHAR :=
SUBSTR(P_TLV_STRG, 1, V_COUNTER_LOOP - 1)
|| SUBSTR(P_TAG_NAME, 1, 3)
|| TO_CHAR(NVL(LENGTH(P_TAG_VALUE), 0), 'FM000')
|| P_TAG_VALUE
|| SUBSTR(P_TLV_STRG, V_COUNTER_LOOP + 6 + TO_NUMBER(SUBSTR(P_TLV_STRG, V_COUNTER_LOOP + 3, 3)));
P_TLV_STRG := TO_CHAR(LENGTH(TMP_VARCHAR), 'FM0000') || TMP_VARCHAR;
RETURN (DECLARATION_CST.OK);
END IF;
V_COUNTER_LOOP := V_COUNTER_LOOP + 6 + TO_NUMBER(SUBSTR(P_TLV_STRG, V_COUNTER_LOOP + 3, 3));
END LOOP;
P_TLV_STRG :=
P_TLV_STRG
|| SUBSTR(P_TAG_NAME, 1, 3)
|| TO_CHAR(NVL(LENGTH(P_TAG_VALUE), 0), 'FM000')
|| P_TAG_VALUE;
P_TLV_STRG := TO_CHAR(LENGTH(P_TLV_STRG), 'FM0000') || P_TLV_STRG;
RETURN 0;
EXCEPTION
WHEN OTHERS THEN
RETURN -1;
END PUT_TAG;
You need to create the mapping table first in the database which maps to the field and its length (in my example I have used CTE as your mapping table: YOUR_MAPPING_TABLE).
Oracle setup:
SQL> CREATE TABLE TEST1234 (
2 FIELD001 VARCHAR2(4000),
3 FIELD002 VARCHAR2(4000),
4 FIELD003 VARCHAR2(4000),
5 FIELD004 VARCHAR2(4000)
6 );
Table created.
Solution query:
SQL> INSERT INTO TEST1234
2 WITH YOUR_MAPPING_TABLE (FIELD_ID, LEN)
3 AS (
4 SELECT 'FIELD001', 4 FROM DUAL UNION ALL
5 SELECT 'FIELD002', 3 FROM DUAL UNION ALL
6 SELECT 'FIELD003', 3 FROM DUAL UNION ALL
7 SELECT 'FIELD004', 12 FROM DUAL
8 )
9 SELECT * FROM
10 ( SELECT M.FIELD_ID, -- Your string will go in following substring
11 SUBSTR('0086037012123456789012', M.STARTPOS + 1, M.TOTALLEN) AS FIELDVALUE
12 FROM ( SELECT FIELD_ID,
13 SUM(LEN) OVER(ORDER BY FIELD_ID ) - LEN AS STARTPOS,
14 LEN AS TOTALLEN
15 FROM YOUR_MAPPING_TABLE
16 ) M
17 ) PIVOT (
18 MIN ( FIELDVALUE )
19 FOR FIELD_ID IN ( 'FIELD001', 'FIELD002', 'FIELD003', 'FIELD004' )
20 );
1 row created.
Testing the result
SQL> SELECT * FROM TEST1234;
FIELD001 | FIELD002 | FIELD003 | FIELD004
---------- | ---------- | ---------- | -------------
0086 | 037 | 012 | 123456789012
SQL>
You can pass your big string in the query as per your logic.

asking for user input in PL/SQL

I am new to PL/SQL and I am stuck on some code.
I am wanting to ask the user for a number and then I want to be able to use this number in an IF THEN statement to verify if the number is greater than or less than 20.
I am stuck on how to obtain user input and once I have it, I can store it and give it back to the user and verify its greater than or less than 20.
DECLARE
a number(2) := 10;
BEGIN
a:= 10;
-- check the boolean condition using if statement
IF( a < 20 ) THEN
-- if condition is true then print the following
dbms_output.put_line('a is less than 20 ' );
END IF;
dbms_output.put_line('value of a is : ' || a);
END;
/
SQL> set serveroutput on; -- for "dbms_output.put_line" to take effect
SQL> DECLARE
a number := &i_nr; -- there's no need to restrict a non-decimal numeric variable to a length
BEGIN
--a:= 10; --no need this when initialization is in declaration section
-- check the boolean condition using if statement
IF( a < 20 ) THEN
-- if condition is true then print the following
dbms_output.put_line('a is less than 20 ' );
END IF;
dbms_output.put_line('value of a is : ' || a);
END;
/
-- it prompts you for value of &i_nr "enter a numeric value, for example 10", for string values it must be in quotes '&i_str'
In SQLPlus, you'd use "&"; in my example, it is && so that I wouldn't have to enter the same value several times (i.e. every time "a" is referenced):
SQL> begin
2 if &&a < 20 then
3 dbms_output.put_line('a is less than 20');
4 end if;
5 dbms_output.put_line('value of a is: ' || &&a);
6 end;
7 /
Enter value for a: 5
old 2: if &&a < 20 then
new 2: if 5 < 20 then
old 5: dbms_output.put_line('value of a is: ' || &&a);
new 5: dbms_output.put_line('value of a is: ' || 5);
a is less than 20
value of a is: 5
PL/SQL procedure successfully completed.
SQL>
Though, I'd say that you'd rather create a procedure with an IN parameter, such as
SQL> create or replace procedure p_test (par_a in number) is
2 begin
3 if par_a < 20 then
4 dbms_output.put_line('a is less than 20');
5 end if;
6 dbms_output.put_line('value of a is: ' || par_a);
7 end;
8 /
Procedure created.
SQL> exec p_test(15);
a is less than 20
value of a is: 15
PL/SQL procedure successfully completed.
SQL> exec p_test(34);
value of a is: 34
PL/SQL procedure successfully completed.
SQL>

to call a funktion with a rowtype Parameter

hello everyone i have the following function:
function Entfernung(v_stadt Rheinlandstädte%rowtype)
Return Abstandstabelle is
tabelle ABSTANDSTABELLE;
cursor c_städte IS
Select *
From Rheinlandstädte
;
v_andereStadt Rheinlandstädte%rowtype;
v_entfernung float;
begin
Open c_städte;
LOOP
fetch c_städte into v_andereStadt;
v_entfernung := Abstand(v_stadt, v_andereStadt);
tabelle(v_andereStadt.stadtname) := v_entfernung;
exit when c_städte%NOTFOUND;
END Loop;
close c_städte;
return tabelle;
end Entfernung;
how can i call this function in oracle. The parameter is rowtype and I cant't use SELECT function from DUAL;
A simple example:
SQL> create table tab_par(a number, b number)
2 /
Table created.
SQL> create or replace type tab_numbers as table of number
2 /
Type created.
SQL> create or replace function fun_par (r IN tab_par%rowtype) return tab_numbers is
2 retVal tab_numbers := new tab_numbers();
3 begin
4 retVal.extend(2);
5 retVal(1) := r.a;
6 retVal(2) := r.b;
7 return retVal;
8 end;
9 /
Function created.
SQL> insert into tab_par values ( 10, 5);
1 row created.
SQL> declare
2 r tab_par%rowtype;
3 n tab_numbers;
4 begin
5 select *
6 into r
7 from tab_par
8 where rownum = 1;
9 -- call your function
10 n := fun_par(r);
11 dbms_output.put_line('n = ' || n(1));
12 dbms_output.put_line('n = ' || n(2));
13 end;
14 /
n = 10
n = 5
PL/SQL procedure successfully completed.
SQL>
here is how I call this function, this function is in the package:
DECLARE
v_stadtname_a VARCHAR(20):='Düsseldorf';
v_stadtA Rheinlandstädte%rowtype;
tabelle GEOGRAPHICAL_PACKAGE.ABSTANDSTABELLE;
v_stadt Rheinlandstädte%rowtype;
CURSOR c_sta IS
SELECT * FROM Rheinlandstädte;
BEGIN
SELECT * INTO v_stadtA
FROM rheinlandstädte
WHERE stadtname=v_stadtname_a;
OPEN c_sta;
LOOP
FETCH c_sta INTO v_stadt;
EXIT WHEN c_sta%NOTFOUND;
tabelle := GEOGRAPHICAL_PACKAGE.Entfernung(v_stadtA);
dbms_output.put_line('Abstand zwischen '||v_stadtA.stadtname||' und '||
v_stadt.stadtname||' beträgt: '||tabelle(v_stadt.stadtname)||' km.');
END LOOP;
CLOSE c_sta;
END;

Resources