How to return multiple values from a function without global types - sql

In an application GUI-Elements can be initialized by using SQL statements.
SELECT name $GUIElement
FROM myTable
WHERE id = 1337;
Since the underlying database structure may change, I am prefering to hide the structure and use an Oracle Package as interface instead. For single values this approach works well with functions like:
SELECT myPackage.getNameByID(1337) $GUIElement
FROM DUAL;
Now I am facing the issue of initializing a list. Original Code:
SELECT name $GUIList
FROM myTable;
To return multiple values from a function I need to define a new SQL type. If the function returns a TABLE OF VARCHAR2 this code works:
SELECT COLUMN_VALUE $GUIList
FROM TABLE(myPackage.getNames())
Unfortunately, I have no rights to create new types on the productive database.
So, is there a way in Oracle to get a similar behaviour without defining a new data type? I have looked into SYS_REFCURSERbut it seems not to work properly.

What about xmltype. The function always returns xml type. And for processing this object you are using xmltable.
Something like this.
create or replace function return_params return xmltype is
result xmltype;
begin
select xmlelement(params,xmlagg(xmlelement(param,xmlforest(name,value,data_type)))) into result from (
select 'param_name1' name, 'param_value1' value , 'varchar2' data_type from dual
union all
select 'param_name2' name, 'param_value2' value , 'varchar2' data_type from dual
union all
select 'param_name3' name, 'param_value3' value , 'varchar2' data_type from dual
); return result;
end;
select * from xmltable('/PARAMS/PARAM' passing return_params
columns name varchar2(1000) path 'NAME'
,value varchar2(1000) path 'VALUE'
,data_type varchar2(1000) path 'DATA_TYPE'
)

Related

convert varchar value to array to varchar sql oracle [duplicate]

I am having trouble getting a block of pl/sql code to work. In the top of my procedure I get some data from my oracle apex application on what checkboxes are checked. Because the report that contains the checkboxes is generated dynamically I have to loop through the
APEX_APPLICATION.G_F01
list and generate a comma separated string which looks like this
v_list VARCHAR2(255) := (1,3,5,9,10);
I want to then query on that list later and place the v_list on an IN clause like so
SELECT * FROM users
WHERE user_id IN (v_list);
This of course throws an error. My question is what can I convert the v_list to in order to be able to insert it into a IN clause in a query within a pl/sql procedure?
If users is small and user_id doesn't contain commas, you could use:
SELECT * FROM users WHERE ',' || v_list || ',' LIKE '%,'||user_id||',%'
This query is not optimal though because it can't use indexes on user_id.
I advise you to use a pipelined function that returns a table of NUMBER that you can query directly. For example:
CREATE TYPE tab_number IS TABLE OF NUMBER;
/
CREATE OR REPLACE FUNCTION string_to_table_num(p VARCHAR2)
RETURN tab_number
PIPELINED IS
BEGIN
FOR cc IN (SELECT rtrim(regexp_substr(str, '[^,]*,', 1, level), ',') res
FROM (SELECT p || ',' str FROM dual)
CONNECT BY level <= length(str)
- length(replace(str, ',', ''))) LOOP
PIPE ROW(cc.res);
END LOOP;
END;
/
You would then be able to build queries such as:
SELECT *
FROM users
WHERE user_id IN (SELECT *
FROM TABLE(string_to_table_num('1,2,3,4,5'));
You can use XMLTABLE as follows
SELECT * FROM users
WHERE user_id IN (SELECT to_number(column_value) FROM XMLTABLE(v_list));
I have tried to find a solution for that too but never succeeded. You can build the query as a string and then run EXECUTE IMMEDIATE, see http://docs.oracle.com/cd/B19306_01/appdev.102/b14261/dynamic.htm#i14500.
That said, it just occurred to me that the argument of an IN clause can be a sub-select:
SELECT * FROM users
WHERE user_id IN (SELECT something FROM somewhere)
so, is it possible to expose the checkbox values as a stored function? Then you might be able to do something like
SELECT * FROM users
WHERE user_id IN (SELECT my_package.checkbox_func FROM dual)
Personally, i like this approach:
with t as (select 'a,b,c,d,e' str from dual)
--
select val
from t, xmltable('/root/e/text()'
passing xmltype('<root><e>' || replace(t.str,',','</e><e>')|| '</e></root>')
columns val varchar2(10) path '/'
)
Which can be found among other examples in Thread: Split Comma Delimited String Oracle
If you feel like swamping in even more options, visit the OTN plsql forums.

Oracle SQL: how to pass parameter to a function that is used in a view through a column in a view?

Let's say we have such view that uses function with hard-coded parameter:
CREATE OR REPLACE VIEW x AS
SELECT t.some_value
FROM table(function(p1 => '1')) t;
If I'd like to pass that parameter to a function through a view, what are possible options? Please mind that using global or context/bind variables is not an option. So far I've came up with an option to use a table that holds all available parameter values (keys) that could be passed to a view:
CREATE OR REPLACE VIEW x AS
SELECT st.input_param,
t.some_value
FROM some_table st
table(function(p1 => st.input_param)) t;
However, I am wondering if there are any other possible options?
You can't pass a parameter to a view but you can use the next alternative:
CREATE TYPE RECORDS_VARCHAR AS TABLE OF VARCHAR2(100);
create or replace function virtual_table( input_param number )
return RECORDS_VARCHAR
PIPELINED
is
begin
FOR a IN (
select '1' AS VALUE from dual where input_param = 2
UNION ALL
select '8' AS VALUE from dual
) loop
pipe row (a.VALUE);
end loop;
return;
end;
SELECT * FROM TABLE(virtual_table(2)); --1,8
SELECT * FROM TABLE(virtual_table(1)); --8

how to return data from the query select * from table in oracle, without using cursor , with an out parameter

how to return data from the query select * from table in oracle, without using cursor , with an out parameter
You have the following options.
Create and store a TYPE OBJECT EXAMPLE_REC with same structure as that of the table and compatible datatypes.
for eg:
CREATE OR REPLACE TYPE EXAMPLE_REC AS OBJECT (
col1 datatype,
col2 datatype,
col3 datatype
);
You then don't need another local record variable.But, you need to initialize using NEW keyword as shown. You can then simply fetch the values into corresponding record elements.
CREATE OR REPLACE PROCEDURE example_sp(
p_example_rec OUT example_rec
)
IS
BEGIN
p_example_rec := NEW example_rec(NULL,NULL,NULL); --Initialization
SELECT
*
INTO
p_example_rec.col1,p_example_rec.col2,p_example_rec.col3
FROM
table;
END;

Oracle SQL, is it possible to use a user-defined datatype property in a nested select?

I have a PL/SQL function that returns a datatype with a property start_date:
create or replace FUNCTION RETURN_OBJ
RETURN my_obj
IS
obj my_obj;
BEGIN
obj := my_obj(SYSDATE);
RETURN obj;
END;
create or replace TYPE my_obj
AS OBJECT (
start_date DATE
);
I can make use of the property in a simple SELECT statement e.g.
select RETURN_OBJ().start_date FROM DUAL
However when I try to use a virtual table (e.g. to avoid multiple function calls) I get an error:
select obj.start_date from (select RETURN_OBJ() AS obj FROM DUAL)
ORA-00904: "OBJ"."START_DATE": invalid identifier
Am I using the wrong syntax, or is this just not possible?
(By the way I'm using Oracle 11 although the customer is still on 9)
Thanks very much.
Brackets :-)
select (obj).start_date from (select RETURN_OBJ() AS obj FROM DUAL)
You must use a table alias when you like to access functions from OBJECT Types. Try this one:
SELECT t.obj.start_date
FROM (SELECT RETURN_OBJ() AS obj FROM DUAL) t;

Is there a way to pass a set of values as a parameter in an Oracle SQL Statement

I would like to pass a set of values as a parameter to an Sql Statement (in vb.net).
In my case:
Users are allowed to upload a set of IDs, to check availability of an item. I would like to execute a statement that will return the items that match any of the IDs by doing something like the following:
SELECT * FROM MyTable WHERE id IN ('123','456','789')
But I cannot pass on the value ('123','456','789') as a parameter as it will be taken as an atomic value - a whole string, i.e., this will not work:
SELECT * FROM MyTable WHERE id IN :param
where :param is ('123','456','789')
I cannot concatenate the strings (as shown above) either to avoid client-side sql injection.
Any ideas?
you could pass the values in as XML and parse them using the XMLDOM.
See: here
DECLARE
vXML VARCHAR2 (10000 CHAR) := '<ids><id>1</id><id>2</id><id>3</id></ids>';
BEGIN
OPEN :refc FOR
SELECT c."id"
FROM XMLTABLE ('/ids/id'
PASSING XMLTYPE (vXML)
COLUMNS "id" VARCHAR2 (32)) c;
END;
From VB.net you can pass an "Associative array" to a SQL call.
In PL/SQL create types and procedures like this:
CREATE OR REPLACE TYPE NUMBER_TABLE_TYPE AS TABLE OF NUMBER;
CREATE OR REPLACE PACKAGE My_Package AS
TYPE NUMBER_ARRAY_TYPE IS TABLE OF NUMBER INDEX BY BINARY_INTEGER;
PROCEDURE My_Procedure(arr IN NUMBER_ARRAY_TYPE);
END My_Package;
CREATE OR REPLACE PACKAGE BODY My_Package AS
PROCEDURE My_Procedure(arr IN NUMBER_ARRAY_TYPE) IS
nested_table NUMBER_TABLE_TYPE := NUMBER_TABLE_TYPE();
BEGIN
-- First transform "Associative array" to a "Nested Table"
FOR i IN arr.FIRST..att.LAST LOOP
nested_table.EXTEND;
nested_table(nested_table.LAST) := arr(i);
END LOOP;
SELECT *
INTO ...
FROM MyTable
WHERE ID MEMBER OF nested_table;
END My_Procedure;
END My_Package;
In VB.NET it looks like this:
Sub My_Sub(ByVal idArr As Long())
Dim cmd As OracleCommand
Dim par As OracleParameter
cmd = New OracleCommand("BEGIN My_Package.My_Procedure(:arr); END;"), con)
cmd.CommandType = CommandType.Text
par = cmd.Parameters.Add("arr", OracleDbType.Int64, ParameterDirection.Input)
par.CollectionType = OracleCollectionType.PLSQLAssociativeArray
par.Value = idArr
par.Size = idArr.Length
cmd.ExecuteNonQuery()
End Sub
Check Oracle doc for further information: PL/SQL Associative Array Binding
The solution to the question is to ultimately build an SQL statement which would look like this (sorry for the images but I could not paste the XML correctly):
In vb.net (or others I suppose) therefore, you would then replace the XML itself with a parameter as follows:
PASSING XMLTYPE(:1)
where :1 in this case would be the XML text:
(Do remember to build the XML text using a StringBuilder or any other efficient XML string builder).
Why can't you just pass it as one atomic value and then work with the INSTR-function Oracle offers.
For Example:
WITH MyTable AS (
SELECT 'abc' ID FROM dual UNION ALL
SELECT 'abcc' ID FROM dual UNION ALL
SELECT 'bbc' ID FROM dual UNION ALL
SELECT 'def' ID FROM dual UNION ALL
SELECT 'abcdef' ID FROM dual)
select * from MyTable where instr('(''abc'', ''def'')', '''' || id || '''') > 0;