Converting data type Number to varchar2 - oracle

I'm currently working on creating a stored procedure in PL SQL that will turn a data type number 4 to the varchar2 to A.
SELECT s.sname, g.sid, s.sid, c.cid, g.cid,
CAST(CASE g.grade
WHEN 4 THEN 'A'
WHEN 3 THEN 'B'
WHEN 2 THEN 'C'
WHEN 1 THEN 'D'
WHEN 0 THEN 'F'
ELSE 'No Value'
END AS varchar2(55)) AS Grades
FROM student s, grades g, class c
WHERE c.cid = g.cid
AND g.sid = s.sid
AND g.sid = s.sid;
This is works as is but once I add in this in a create procedure it errors out as Unexpected error
Error code -6502: ORA-06502: PL/SQL: numeric or value error:character to number conversion error.
Also, I have tried adding the below within the procedure and return the same error.
EXIT WHEN c1%NOTFOUND; -- exit check
dbms_output.put_line('Student Name: '|| s_sname);
dbms_output.put_line('Class Name: ' || c_cname);
dbms_output.put_line('Grade: ' || to_char(g_grade, 'ABCDF');
dbms_output.put_line('-----------------');
In the above I'm trying to print the letter grade and not the numerical grade.

You need to paste the entire code. It works for me.
create table testing(i int);
insert into testing values (0);
insert into testing values (1);
insert into testing values (2);
insert into testing values (3);
create or replace procedure p
is
begin
for i in (select CAST(CASE g.grade
WHEN 4 THEN 'A'
WHEN 3 THEN 'B'
WHEN 2 THEN 'C'
WHEN 1 THEN 'D'
WHEN 0 THEN 'F'
ELSE 'No Value'
END AS varchar2(55)) AS Grades
from testing
)
loop
dbms_output.put_line(i.Grades);
end loop;
end;
/
set serveroutput on;
begin
p;
end;
/
D
C
F
B
PL/SQL procedure successfully completed.

Related

Show the output of a procedure in Oracle

The procedure uses the previous function to display the list of the products: num, designation and mention on the application.
SET SERVEROUTPUT ON;
CREATE OR REPLACE FUNCTION STORE(num_produit IN INTEGER) RETURN VARCHAR AS
N INTEGER := 0;
incre INTEGER := 0;
BEGIN
SELECT SUM(qte) INTO N FROM Ligne_Fact WHERE num_produit = produit;
IF N > 15 THEN
RETURN 'fort';
ELSIF N > 11 THEN
RETURN 'moyen';
END IF;
RETURN 'faible';
END;
/
CREATE OR REPLACE PROCEDURE SHOW_PRODUITS IS
SOME_VAR VARCHAR2(256);
BEGIN
SELECT num, designation, STORE(num) INTO SOME_VAR FROM Produit;
dbms_output.enable();
dbms_output.put_line('result : '|| SOME_VAR);
END;
/
BEGIN
SHOW_PRODUITS;
END;
/
I am sure that all the tables are filled with some dummy data, but I am getting the following error:
Function STOCKER compiled
Procedure AFFICHER_PRODUITS compiled
LINE/COL ERROR
--------- -------------------------------------------------------------
4/4 PL/SQL: SQL Statement ignored
4/56 PL/SQL: ORA-00947: not enough values
Errors: check compiler log
Error starting at line : 28 in command -
BEGIN
AFFICHER_PRODUITS;
END;
Error report -
ORA-06550: line 2, column 5:
PLS-00905: object SYSTEM.SHOW_PRODUITS is invalid
ORA-06550: line 2, column 5:
PL/SQL: Statement ignored
06550. 00000 - "line %s, column %s:\n%s"
*Cause: Usually a PL/SQL compilation error.
The first major mistake you've made is to create your objects in SYSTEM schema. It, just like SYS, are special and should be used only for system maintenance. Create your own user and do whatever you're doing there.
As of your question: select returns 3 values, but you're trying to put them into a single some_var variable. That won't work. Either add another local variables (for num and designation), or remove these columns from the select:
CREATE OR REPLACE PROCEDURE SHOW_PRODUITS IS
SOME_VAR VARCHAR2(256);
BEGIN
SELECT STORE(num) INTO SOME_VAR FROM Produit; --> here
dbms_output.enable();
dbms_output.put_line('result : '|| SOME_VAR);
END;
/
Code, as you put it, presumes that produit contains a single record (it can't be empty nor it can have 2 or more rows because you'll get various errors).
Maybe you wanted to access all rows; in that case, consider using a loop, e.g.
CREATE OR REPLACE PROCEDURE SHOW_PRODUITS IS
SOME_VAR VARCHAR2(256);
BEGIN
FOR cur_r IN (SELECT num, designation, STORE(num) some_var FROM Produit) LOOP
dbms_output.put_line(cur_r.num ||', '|| cur_r.designation ||', result : '|| cur_r.some_var);
END LOOP;
END;
/
Then
set serveroutput on
BEGIN
SHOW_PRODUITS;
END;
/
You are trying to compile some stored routine which calls another stored routine named AFFICHER_PRODUITS and that routine calls SHOW_PRODUITS but SHOW_PRODUITS does not compile, hence the error. (By the way, it is recommended not to create your own stored routines in the SYSTEM schema.)
SHOW_PRODUITS does not compile because of this line:
SELECT num, designation, STORE(num) INTO SOME_VAR FROM Produit;
It appears that you want to get the query results as a string. In order to do that, you need to concatenate the column values, i.e.
SELECT num || designation || STORE(num) INTO SOME_VAR FROM Produit;
Of-course if all you want to do is display the query results, using DBMS_OUTPUT, you can declare a separate variable for each column.
CREATE OR REPLACE PROCEDURE SHOW_PRODUITS IS
SOME_NUM Produit.num%type;
SOME_DES Produit.designation%type;
SOME_VAR VARCHAR2(6);
BEGIN
SELECT num
,designation
,STORE(num)
INTO SOME_NUM
,SOME_DES
,SOME_VAR
FROM Produit;
dbms_output.enable();
dbms_output.put_line('result : ' || SOME_NUM || SOME_DES || SOME_VAR);
END;
Note that if the query returns more than one row, you will [probably] need to use a cursor.
CREATE OR REPLACE PROCEDURE SHOW_PRODUITS IS
SOME_NUM Produit.num%type;
SOME_DES Produit.designation%type;
SOME_VAR VARCHAR2(6);
--
CURSOR c1 IS
SELECT num, designation, STORE(num) FROM Produit;
BEGIN
OPEN c1;
LOOP
FETCH c1 INTO SOME_NUM, SOME_DES, SOME_VAR;
EXIT WHEN c1%NOTFOUND;
dbms_output.put_line('result : ' || SOME_NUM || SOME_DES || SOME_VAR);
END LOOP;
CLOSE c1;
END;

Oracle: detect exception in type construtor during bulk collect

Environment: Oracle 11g
I have a type ty1 with some arguments like s1, s2.
If I use it like this:
SELECT ty1(s1,s2) BULK COLLECT INTO l_collection_of_ty1 FROM ...
I get a collection of ty1.
Now if there is an exception raised inside one of the constructor calls of ty1 the corresponding element of my collection is set to NULL, but the overall SELECT works (no exception, collection returned).
My question, can I detect this right after the SELECT without having to loop over the collection? Is there maybe even a way to access the original error message in a similar way as SQL%BULK_EXCEPTION does for DML?
One workaround I thought of is to not use the constructor during BULK COLLECT, but read collections of s1 and s2, then construct the TYPE in my own loop where I can handle the exception, but that is much more code and I would prefer it if Oracle has some build in way.
here is a testcase that demonstrates that all exceptions are thrown through a bulk-select, but NO_DATA_FOUND is dropped.
-- remember to retreive dbms_output
SET SERVEROUTPUT ON
CREATE OR REPLACE FUNCTION p1 (v1 PLS_INTEGER)
RETURN NUMBER
AS
BEGIN
CASE v1
WHEN 1
THEN
RAISE ACCESS_INTO_NULL;
WHEN 2
THEN
RAISE CASE_NOT_FOUND;
WHEN 3
THEN
RAISE COLLECTION_IS_NULL;
WHEN 4
THEN
RAISE CURSOR_ALREADY_OPEN;
WHEN 5
THEN
RAISE DUP_VAL_ON_INDEX;
WHEN 6
THEN
RAISE INVALID_CURSOR;
WHEN 7
THEN
RAISE INVALID_NUMBER;
WHEN 8
THEN
RAISE LOGIN_DENIED;
WHEN 9
THEN
RAISE NO_DATA_FOUND;
WHEN 10
THEN
RAISE NOT_LOGGED_ON;
WHEN 11
THEN
RAISE PROGRAM_ERROR;
WHEN 12
THEN
RAISE ROWTYPE_MISMATCH;
WHEN 13
THEN
RAISE SELF_IS_NULL;
WHEN 14
THEN
RAISE STORAGE_ERROR;
WHEN 15
THEN
RAISE SUBSCRIPT_BEYOND_COUNT;
WHEN 16
THEN
RAISE SUBSCRIPT_OUTSIDE_LIMIT;
WHEN 17
THEN
RAISE SYS_INVALID_ROWID;
WHEN 18
THEN
RAISE TIMEOUT_ON_RESOURCE;
WHEN 19
THEN
RAISE TOO_MANY_ROWS;
WHEN 20
THEN
RAISE VALUE_ERROR;
WHEN 21
THEN
RAISE ZERO_DIVIDE;
ELSE
RETURN v1;
END CASE;
END;
/
DECLARE
TYPE type1 IS TABLE OF NUMBER;
col1 type1;
BEGIN
FOR ii IN 1 .. 22
LOOP
BEGIN
SELECT p1 (ii)
BULK COLLECT INTO col1
FROM DUAL;
IF col1 (1) IS NULL
THEN
DBMS_OUTPUT.put_line (TO_CHAR (ii, '00') || ': NULL');
ELSE
DBMS_OUTPUT.put_line (TO_CHAR (ii, '00') || ': ' || col1 (1));
END IF;
EXCEPTION
WHEN OTHERS
THEN
DBMS_OUTPUT.put_line (
TO_CHAR (ii, '00')
|| ': exception '
|| SQLCODE);
END;
END LOOP;
END;
/
I wrote a small test case for you and the exception has to be swallowed inside ty1 and not raised, because otherwise the select would not finish successfully:
create or replace function p1 (v1 number) return number
as
begin
if v1 = 1 then
return 1;
elsif v1 = 2 then
raise_application_error(-20010,'err 2');
else
raise_application_error(-20010,'err 3');
end if;
end;
/
declare
type type1 is table of number;
col1 type1;
begin
select p1(level) bulk collect into col1 from dual connect by level <=3;
end;
/
Result:
Error report -
ORA-20010: err 2
So my suggestion to you is - if you want to stay close to your solution - that at the place where you handle the exception in ty1, you write the exceptions to a table. You can then access this table to find the exceptions and don't need to loop through the whole collection. But honestly, what's wrong with looping in PL/SQL over a collection, it's all in memory? HTH
I see only two options.
An exception is consumed inside constructor.
The another constructor is in use.
Example:
create or replace type ty1 as object (p1 number
, constructor function ty1 (p1 varchar2)
return self as result);
create or replace type body ty1
is
constructor function ty1 (p1 varchar2)
return self as result is
x number;
begin
raise_application_error(-20000,'Always Exception');
return;
end;
end;
Test 1 no exception:
declare
type l_collection_of_ty1 is table of ty1;
a varchar2(4000);
x l_collection_of_ty1;
begin
--test1 select ty1(level) bulk collect into x from dual connect by level < 10;
-- no exceptions
--test2 select ty1(level||1) bulk collect into x from dual connect by level < 10;
-- exceptions
--test3 select ty1(null) bulk collect into x from dual connect by level < 10;
-- exceptions
end;
In Test1 db is using Attribute-Value Constructor. It is generate by default.
In Test2 db is using user defined constructor.
In Test3 db is not able to find out which constructor should be used.()

How to access and query objects passed as parameter to a procedure while converting from Oracle to postgresql

I have a procedure in Oracle that I need to convert to Postgresql and need help on it. It paases a collection of objects in a procedure.The procedure then checks if each object is present in a database table or not and if present it gives a message that , that specific element is found/present. if some element that is paassed to the procedure is not present in the table, the procedure just doesnt do anything. I have to write equivalent of that in postgresql. I think the heart of the issue is this statement:
SELECT COUNT (*)
INTO v_cnt
FROM **TABLE (p_cust_tab_type_i)** pt
WHERE pt.ssn = cc.ssn;
In Oracle a collection can be treated as a table and one can query it but I dont know how to do that in postgresql. The code to create the table, add data, create the procedure, call the procedure by passing the collection (3 objects) and output of that is posted below. Can someone suggest how this can be done in postgresql?
Following the oracle related code and details:
--create table
create table temp_n_tab1
(ssn number,
fname varchar2(20),
lname varchar2(20),
items varchar2(100));
/
--add data
insert into temp_n_tab1 values (1,'f1','l1','i1');
--SKIP no. ssn no. 2 intentionally..
insert into temp_n_tab1 values (3,'f3','l3','i3');
insert into temp_n_tab1 values (4,'f4','l4','i4');
insert into temp_n_tab1 values (5,'f5','l5','i5');
insert into temp_n_tab1 values (6,'f6','l6','i6');
commit;
--create procedure
SET SERVEROUTPUT ON
CREATE OR REPLACE PROCEDURE temp_n_proc (
p_cust_tab_type_i IN temp_n_customer_tab_type)
IS
t_cust_tab_type_i temp_n_customer_tab_type;
v_cnt NUMBER;
v_ssn temp_n_tab1.ssn%TYPE;
CURSOR c
IS
SELECT ssn
FROM temp_n_tab1
ORDER BY 1;
BEGIN
--t_cust_tab_type_i := p_cust_tab_type_i();
FOR cc IN c
LOOP
SELECT COUNT (*)
INTO v_cnt
FROM TABLE (p_cust_tab_type_i) pt
WHERE pt.ssn = cc.ssn;
IF (v_cnt > 0)
THEN
DBMS_OUTPUT.put_line (
'The array element '
|| TO_CHAR (cc.ssn)
|| ' exists in the table.');
END IF;
END LOOP;
EXCEPTION
WHEN OTHERS
THEN
DBMS_OUTPUT.PUT_LINE (SQLERRM);
END;
/
--caller proc
SET SERVEROUTPUT ON
declare
array temp_n_customer_tab_type := temp_n_customer_tab_type();
begin
for i in 1 .. 3
loop
array.extend;
array(i) := temp_n_cust_header_type( i, 'name ' || i, 'lname ' || i,i*i*i*i );
end loop;
temp_n_proc( array );
end;
/
caller proc output:
The array element 1 exists in the table.
The array element 3 exists in the table.
When you create a table in Postgres, a type with the same name is also created. So you can simply pass an array of the table's type as a parameter to the function.
Inside the function you can then use unnest() to treat the array like a table.
The following is the closest match to your original Oracle code:
create function temp_n_proc(p_cust_tab_type_i temp_n_tab1[])
returns void
as
$$
declare
l_rec record;
l_msg text;
l_count integer;
BEGIN
for l_rec in select t1.ssn
from temp_n_tab1 t1
loop
select count(*)
into l_count
from unnest(p_cust_tab_type_i) as t
where t.ssn = l_rec.ssn;
if l_count > 0 then
raise notice 'The array element % exist in the table', l_rec.ssn;
end if;
end loop;
END;
$$
language plpgsql;
The row-by-row processing is not a good idea to begin with (neither in Postgres, nor in Oracle). It would be a lot more efficient to get the existing elements in a single query:
create function temp_n_proc(p_cust_tab_type_i temp_n_tab1[])
returns void
as
$$
declare
l_rec record;
l_msg text;
BEGIN
for l_rec in select t1.ssn
from temp_n_tab1 t1
where t1.ssn in (select t.ssn
from unnest(p_cust_tab_type_i) as t)
loop
raise notice 'The array element % exist in the table', l_rec.ssn;
end loop;
return;
END;
$$
language plpgsql;
You can call the function like this:
select temp_n_proc(array[row(1,'f1','l1','i1'),
row(2,'f2','l2','i2'),
row(3,'f3','l3','i3')
]::temp_n_tab1[]);
However a more "Postgres" like and much more efficient way would be to not use PL/pgSQL for this, but create a simple SQL function that returns the messages as a result:
create or replace function temp_n_proc(p_cust_tab_type_i temp_n_tab1[])
returns table(message text)
as
$$
select format('The array element %s exist in the table', t1.ssn)
from temp_n_tab1 t1
where t1.ssn in (select t.ssn
from unnest(p_cust_tab_type_i) as t)
$$
language sql;
This returns the output of the function as a result rather than using the clumsy raise notice.
You can use it like this:
select *
from temp_n_proc(array[row(1,'f1','l1','i1'),
row(2,'f2','l2','i2'),
row(3,'f3','l3','i3')
]::temp_n_tab1[]);

PL/SQL Oracle. Best way to implement an IS_CONTAINED operator

I am newbie so that maybe this question has been made one or two million times, but it is not findable / searchable in the knowledge database.
In Oracle PL/SQL, it is normal to query as follows:
select a,b,c
from table_foo
where c in (select k from table(array_bar));
But I need all the opposite of that. I need a kind of "IS_CONTAINED" operator, like this:
select a,b,c
from table_foo
where AT_LEAST_ONE_OF_THE_ITEMS_IN (select k from table(array_bar)) IS_CONTAINED_IN c;
I have my own ideas to implement it using a function with a loop. But maybe some genius has found a simple way to do it without a function. This is to say, maybe the operator IS_CONTAINED is already invented by Oracle and I haven't found it out.
Sorry if this question is repeated. I promise I have searched for it in the knowledge base. But it seems that nobody in the space-time of this Universe has never needed the super-obvious operator IS_CONTAINED.
SOLUTION:
Thanks to everybody for the suggestions. In the end, I had to use some functions, but I think I got a good solution. The situation is: I have a table of centers. Each center can be in one or more cities, this is to say, it's a 1 to N relationship. But this relationship is done using a single table. This table contains some fields. One of these fields, named 'cities_list', contains all related cities separated by semicolons. It's like this:
CODE DESCRIPTION CITIES_LIST
---- ----------- -----------
0001 Desc 0001 London; Berlin; NY; SF
0002 Desc 0002 Paris; Madrid; Rome
0003 Desc 0003 Berlin; Paris; London
0004 Desc 0004 Madrid;NY;Tokyo
0005 Repe 0005 Rome;Rome;Rome;LA;LA;LA;
0006 One 0006 NY
0007 Desc 0007 Sydney;Tokyo;Madrid
0008 Desc 0008 LA;SF;NY
0009 Desc 0009 Seoul;Beijing;
0010 Error0010 Beijing;;;;OZ;
0011 None 0011 (null)
0012 All 0012 London;Paris;Berlin;Madrid;Rome;NY;SF;LA;Seoul;Beijing;Tokyo;Sydney
Possible cities are: London; Paris; Berlin; Madrid; Rome; NY; SF; LA; Seoul; Beijing; Tokyo; Sydney.
In order to filter records of that table, the user can select, through a combo, one or more of those cities. Selected cities are passed to the PL/SQL query as a string (varchar) of cities separated by a hash sign (#). For instance 'London#Paris#Sydney'.
The PL/SQL has to select the records that have at least one city in common between the field 'cities_list' and the string of cities passed from the combo. First, I put here the PL/SQL code and I will explain it later on:
--1.SELECT AND EXECUTE THIS:
SET SERVEROUTPUT ON;
--2.SELECT AND EXECUTE THIS:
DROP TABLE table_centers; CREATE GLOBAL TEMPORARY TABLE table_centers (code VARCHAR2(10), description VARCHAR2(100), cities_list VARCHAR2(1000));
--3.SELECT AND EXECUTE THIS:
CREATE OR REPLACE TYPE table_TYPE IS TABLE OF VARCHAR2(250);
--4.SELECT AND EXECUTE THIS:
CREATE OR REPLACE FUNCTION VARCHAR_TO_TABLE (input_varchar VARCHAR2, separator VARCHAR2 DEFAULT ';')
RETURN table_TYPE
IS
--VARS
output_table table_TYPE := table_TYPE();
BEGIN
--For better performance, input_varchar is splitted without blanks into output_table using the regular expression [^;]+
SELECT
--The Keyword 'level' in statement 'regexp_substr' refers to a pseudocolumn in Oracle
TRIM(regexp_substr(input_varchar,'[^' || separator || ']+', 1, level))
BULK COLLECT INTO
output_table
FROM DUAL
CONNECT BY
regexp_substr(input_varchar,'[^' || separator || ']+', 1, level) IS NOT NULL;
--Now we have all chunks into the table output_table
RETURN output_table;
END VARCHAR_TO_TABLE;
--5.SELECT AND EXECUTE THIS:
CREATE OR REPLACE FUNCTION INTERSECT_TABLES(input_A VARCHAR2 , separator_A VARCHAR2 , input_B VARCHAR2 , separator_B VARCHAR2)
RETURN NUMBER
IS
--VARS
A table_TYPE;
B table_TYPE;
result BOOLEAN;
BEGIN
--Splits input_A and input_B into tables and checks if there is overlapping
A := VARCHAR_TO_TABLE(input_A, separator_A);
B := VARCHAR_TO_TABLE(input_B, separator_B);
--If intersection is not empty result is TRUE
result := A multiset intersect B is not empty;
-- Returns 1 if intersection is not empty, returns 0 otherwise (Note that functions called from a SQL query cannot take any BOOLEAN parameters)
IF result = TRUE THEN RETURN 1; ELSE RETURN 0; END IF;
END INTERSECT_TABLES;
--6.SELECT AND EXECUTE THIS:
CREATE OR REPLACE PROCEDURE GET_CENTERS (cities_input VARCHAR2 , separator_input VARCHAR2 , out_Cursor OUT sys_refcursor)
AS
BEGIN
OPEN out_Cursor FOR
SELECT tc.code, tc.description, tc.cities_list
FROM table_centers tc
--Has current record some city in common with cities_input? If yes, select current record
WHERE INTERSECT_TABLES(cities_input , separator_input , tc.cities_list , ';') = 1;
END GET_CENTERS;
--7.SELECT AND EXECUTE THIS:
BEGIN
DELETE FROM table_centers; COMMIT;
INSERT ALL
--We'll use following cities: London Paris Berlin Madrid Rome NY SF LA Seoul Beijing Tokyo Sydney
INTO table_centers (code,description,cities_list) VALUES ('0001', 'Desc 0001', 'London; Berlin; NY; SF')
INTO table_centers (code,description,cities_list) VALUES ('0002', 'Desc 0002', 'Paris; Madrid; Rome')
INTO table_centers (code,description,cities_list) VALUES ('0003', 'Desc 0003', 'Berlin; Paris; London')
INTO table_centers (code,description,cities_list) VALUES ('0004', 'Desc 0004', 'Madrid;NY;Tokyo')
INTO table_centers (code,description,cities_list) VALUES ('0005', 'Repe 0005', 'Rome;Rome;Rome;LA;LA;LA;')
INTO table_centers (code,description,cities_list) VALUES ('0006', 'One 0006', 'NY')
INTO table_centers (code,description,cities_list) VALUES ('0007', 'Desc 0007', 'Sydney;Tokyo;Madrid')
INTO table_centers (code,description,cities_list) VALUES ('0008', 'Desc 0008', 'LA;SF;NY')
INTO table_centers (code,description,cities_list) VALUES ('0009', 'Desc 0009', 'Seoul;Beijing;')
INTO table_centers (code,description,cities_list) VALUES ('0010', 'Error0010', 'Beijing;;;;OZ;')
INTO table_centers (code,description,cities_list) VALUES ('0011', 'None 0011', '')
INTO table_centers (code,description,cities_list) VALUES ('0012', 'All 0012', 'London;Paris;Berlin;Madrid;Rome;NY;SF;LA;Seoul;Beijing;Tokyo;Sydney')
SELECT 1 FROM DUAL;
END;
--8.SELECT AND EXECUTE THIS:
SELECT * FROM table_centers;
I have used 'Oracle SQL Developer'. You can select the sentences one by one and execute them with the F9 key. You can also create a Package.
If someone wants to test that code, you can also select and execute with F9 the following query:
--9.SELECT AND EXECUTE THIS:
DECLARE
--VARS
out_Cursor sys_refcursor;
cities_array table_TYPE;
citiesA varchar(1000) := 'London#Paris#Berlin#Madrid#Rome#NY#SF#LA# Seoul # Beijing # Tokyo # Sydney ';
citiesB varchar(1000) := 'London;Paris;Berlin;Madrid;Rome;NY;SF;LA; Seoul ; Beijing ; Tokyo ; Sydney ';
Rcode table_centers.code%TYPE;
Rdescription table_centers.description%TYPE;
Rcities_list table_centers.cities_list%TYPE;
CR char := CHR(13);
TAB char := CHR(9);
BEGIN
--TEST 1
dbms_output.put_line('TEST 1: ' || CR);
cities_array := table_TYPE();
cities_array := VARCHAR_TO_TABLE(citiesA, '#');
--Now we have all cities in the array cities_array
FOR elem in 1 .. cities_array.count LOOP
dbms_output.put_line(TAB || elem || ':' || cities_array(elem) || '.');
END LOOP;
--TEST 2
dbms_output.put_line('TEST 2: ' || CR);
cities_array := table_TYPE();
cities_array := VARCHAR_TO_TABLE(citiesB, ';');
--Now we have all cities in the array cities_array
FOR elem in 1 .. cities_array.count LOOP
dbms_output.put_line(TAB || elem || ':' || cities_array(elem) || '.');
END LOOP;
--TEST 3
dbms_output.put_line('TEST 3: ' || CR);
GET_CENTERS(citiesA, '#', out_Cursor);
fetch out_Cursor into Rcode,Rdescription,Rcities_list;
WHILE out_Cursor%FOUND LOOP
dbms_output.put_line(TAB || 'CITIES:' || Rcities_list || '.');
fetch out_Cursor into Rcode,Rdescription,Rcities_list;
END LOOP;
close out_Cursor;
--TEST 4
dbms_output.put_line('TEST 4: ' || CR);
GET_CENTERS('London#Paris#Sydney', '#', out_Cursor);
fetch out_Cursor into Rcode,Rdescription,Rcities_list;
WHILE out_Cursor%FOUND LOOP
dbms_output.put_line(TAB || 'CITIES:' || Rcities_list || '.');
fetch out_Cursor into Rcode,Rdescription,Rcities_list;
END LOOP;
close out_Cursor;
--TEST 5
dbms_output.put_line('TEST 5: ' || CR);
GET_CENTERS('Madrid', '#', out_Cursor);
fetch out_Cursor into Rcode,Rdescription,Rcities_list;
WHILE out_Cursor%FOUND LOOP
dbms_output.put_line(TAB || 'CITIES:' || Rcities_list || '.');
fetch out_Cursor into Rcode,Rdescription,Rcities_list;
END LOOP;
close out_Cursor;
--TEST 6
dbms_output.put_line('TEST 6: ' || CR);
GET_CENTERS('Gotham City', '#', out_Cursor);
fetch out_Cursor into Rcode,Rdescription,Rcities_list;
WHILE out_Cursor%FOUND LOOP
dbms_output.put_line(TAB || 'CITIES:' || Rcities_list || '.');
fetch out_Cursor into Rcode,Rdescription,Rcities_list;
END LOOP;
close out_Cursor;
--TEST 7
dbms_output.put_line('TEST 7: ' || CR);
GET_CENTERS('', '#', out_Cursor);
fetch out_Cursor into Rcode,Rdescription,Rcities_list;
WHILE out_Cursor%FOUND LOOP
dbms_output.put_line(TAB || 'CITIES:' || Rcities_list || '.');
fetch out_Cursor into Rcode,Rdescription,Rcities_list;
END LOOP;
close out_Cursor;
END;
You can modify TEST 7 and put your own values in the first parameter of the function 'GET_CENTERS'. I have executed this query and I have got these results:
TEST 1:
1:London.
2:Paris.
3:Berlin.
4:Madrid.
5:Rome.
6:NY.
7:SF.
8:LA.
9:Seoul.
10:Beijing.
11:Tokyo.
12:Sydney.
TEST 2:
1:London.
2:Paris.
3:Berlin.
4:Madrid.
5:Rome.
6:NY.
7:SF.
8:LA.
9:Seoul.
10:Beijing.
11:Tokyo.
12:Sydney.
TEST 3:
CITIES:London; Berlin; NY; SF.
CITIES:Paris; Madrid; Rome.
CITIES:Berlin; Paris; London.
CITIES:Madrid;NY;Tokyo.
CITIES:Rome;Rome;Rome;LA;LA;LA;.
CITIES:NY.
CITIES:Sydney;Tokyo;Madrid.
CITIES:LA;SF;NY.
CITIES:Seoul;Beijing;.
CITIES:Beijing;;;;OZ;.
CITIES:London;Paris;Berlin;Madrid;Rome;NY;SF;LA;Seoul;Beijing;Tokyo;Sydney.
TEST 4:
CITIES:London; Berlin; NY; SF.
CITIES:Paris; Madrid; Rome.
CITIES:Berlin; Paris; London.
CITIES:Sydney;Tokyo;Madrid.
CITIES:London;Paris;Berlin;Madrid;Rome;NY;SF;LA;Seoul;Beijing;Tokyo;Sydney.
TEST 5:
CITIES:Paris; Madrid; Rome.
CITIES:Madrid;NY;Tokyo.
CITIES:Sydney;Tokyo;Madrid.
CITIES:London;Paris;Berlin;Madrid;Rome;NY;SF;LA;Seoul;Beijing;Tokyo;Sydney.
TEST 6:
TEST 7:
CITIES:.
The nub of the issue is the function 'INTERSECT_TABLES'. This function uses the sentence " result := A multiset intersect B is not empty; ". A and B are variables of type 'TABLE'. The operator '... multiset intersect ... is not empty' returns TRUE if tables A and B have at least one item (row) with the same value (text or number), regardless of its order or position in each table.
EXPLANATION:
I have created a temporary table named 'table_centers' and I have filled it in with some data. In order to query this table, I have created following functions:
The function 'VARCHAR_TO_TABLE' converts a string (varchar) into a 'table' type variable. You must pass a separator character as a parameter, so that each chunk of the string separated by that character will be one item (=row) of the resulting table. This way, I can use the same function regardless whether cities are separated by a semicolon (;) or by a hash (#). This function uses 'regexp_substr' and BULK COLLECT instead of a LOOP for better performance. The Keyword 'level' in statement 'regexp_substr' refers to a pseudocolumn in Oracle. See Is there a function to split a string in PL/SQL?.
In order to execute the final query to 'table_centers', I have implemented the function 'GET_CENTERS'. It has only one SELECT that selects the records of 'table_centers' that have in their field 'cities_list' at least one city in common with the string 'cities_input', which is passed as a parameter. Both strings are compared by the function 'INTERSECT_TABLES', being these strings previously splitted into tables through the function 'VARCHAR_TO_TABLE'.
The function 'INTERSECT_TABLES' is used in the clause 'WHERE' because the filtering must be done through this function. This is because a 'table' type can not be used inside a SQL query. Otherwise, you'll get an error "collection types can not be used inside a SQL statement". Therefore, using this function in the WHERE clause is mandatory. Also, boolean types can not be used, therefore, the function 'INTERSECT_TABLES' returns the numbers 0 or 1, not FALSE or TRUE.
Perhaps you are looking for multiset conditions. For example:
create or replace type number_tt as table of number;
select 'Yes' as member
from dual
where 1 member of number_tt(1,2,3);
select 'Yes' as subset
from dual
where number_tt(2,3) submultiset of number_tt(1,2,3,4);
Taking William Robertson's answer a step further, to check if at least one member of a set is a member in another set:
create or replace type number_tt as table of number;
/
with t1(id, c) as (
select 1, number_tt(1,2,3) from dual union all
select 2, number_tt(4,5,6) from dual union all
select 3, number_tt(7,8,9) from dual
)
select id, 'Yes' Intersects
from t1
where c multiset intersect number_tt(1,2,3,8) is not empty;
Yields the following results:
ID INTESECTS
1 Yes
3 Yes
Updating based on the sample data provided. Note: converting from string data to sets is left as an exercise for the student ;)
create or replace type varchar30_tt as table of varchar2(30);
/
with t1(id, c) as (
select 1, varchar30_tt('Rome','NY','London') c from dual union all
select 2, varchar30_tt('LA','SF','Torronto') c from dual union all
select 3, varchar30_tt('Paris','London','Rome') c from dual
)
select id
, 'Yes' Intesects
from t1
where c multiset intersect varchar30_tt('SF','LA','NY') is not empty;
You need OR cond -
with array_bar as (select k from table(array_bar))
select a,b,c
from table_foo
where c in array_bar
or b in array_bar
or a in array_bar;

how to look up another field in Oracle Function

Table 1
ID
----------
1
2
3
4
5
Table 2
ID Desc
------------------------------
A1 Apple
A2 Pear
A3 Orange
I am trying to create a Function in Oracle, so that it add the prefix 'A' in Table 1, and after that I want to look up in Table 2 to get the DESC returned. It has to be a function.
Thank you!!!
You may use the following for creation of such a function :
Create or Replace Function Get_Fruit( i_id table2.description%type )
Return table2.description%type Is
o_desc table2.description%type;
Begin
for c in ( select description from table2 where id = 'A'||to_char(i_id) )
loop
o_desc := c.description;
end loop;
return o_desc;
End;
where
no need to include exception handling, because of using cursor
instead of select into clause.
using table_name.col_name%type for declaration of data types for
arguments or variables makes the related data type of the columns
dynamic. i.e. those would be able to depend on the data type of the
related columns.
the reserved keywords such as desc can not be used as column names
of tables, unless they're expressed in double quotes ("desc")
To call that function, the following might be preferred :
SQL> set serveroutput on
SQL> declare
2 i_id pls_integer := 1;
3 o_fruit varchar2(55);
4 begin
5 o_fruit := get_fruit( i_id );
6 dbms_output.put_line( o_fruit );
7 end;
8 /
Apple
PL/SQL procedure successfully completed
I am not sure with your question- Are you trying to achieve something like this:-
CREATE OR REPLACE FUNCTION Replace_Value
(
input_ID IN VARCHAR2
) RETURN VARCHAR2
AS
v_ID varchar(2);
BEGIN
begin
SELECT distinct a.ID into v_id from Table 2 a where a.ID in (select 'A'||b.id from table1 b where b.id=input_ID);
exception
when others then
dbms_output.put_line(sqlcode);
end;
RETURN v_id;
END Replace_Value;
Are you trying for something like this?
CREATE OR replace FUNCTION replace_value (table_name IN VARCHAR2,
input_id IN INTEGER)
RETURN VARCHAR2
AS
v_desc VARCHAR(20);
BEGIN
SELECT descr
INTO v_desc
FROM table2
WHERE id = 'A' || input_id
AND ROWNUM = 1; -- only needed if there are multiple rows for each id.
RETURN v_desc;
END replace_value;
You may also add an exception handling for NO_DATA_FOUND or INVALID_NUMBER

Resources