How to convert this select statement into a stored procedure? - sql

I have this select statement that displays the text (content) of a pl/sql script :
select text
from DBA_source
where type like '%PROCEDURE%' and name like '%JOB_HISTORY%'
order by line;
I want to convert this into a stored procedure in such a way that the 'name' condition of the select statement should be taken as an input and should not be pre defined as show like '%job_history%.
How can this be done ?

This is a function, not a stored procedure:
create type proc_tab is table of DBA_source.text%type;
/
create function select_procedure(p_name in film.title%type) return proc_tab
is
l_proc_tab proc_tab := proc_tab();
n integer := 0;
begin
select text
bulk collect into l_proc_tab
from DBA_source
where type like '%PROCEDURE%' and name like '%' || p_name || '%'
order by line;
return l_proc_tab;
end;
/
I haven't tested this code, but it should at least be a good start.

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 function with select all from tables

SELECT DISTINCT L.* FROM LABALES L , MATCHES M
WHERE M.LIST LIKE '%ENG'
ORDER BY L.ID
I need to create function with this select, I tried this but it doesn't work.
CREATE OR REPLACE FUNCTION getSoccerLists
RETURN varchar2 IS
list varchar2(2000);
BEGIN
SELECT DISTINCT L.* FROM LABALES L , MATCHES M
WHERE M.LIST LIKE '%ENG'
ORDER BY L.ID
return list;
END;
How will I create function that returns all from table L.
Thanks
You may use implicit result using DBMS_SQL.RETURN_RESULT(Oracle12c and above) in a procedure using a cursor to your query.
CREATE OR REPLACE PROCEDURE getSoccerLists
AS
x SYS_REFCURSOR;
BEGIN
OPEN x FOR SELECT DISTINCT L.* FROM LABALES L
JOIN MATCHES M ON ( 1=1 ) -- join condition
WHERE M.LIST LIKE '%ENG'
ORDER BY L.ID;
DBMS_SQL.RETURN_RESULT(x);
END;
/
then simply call the procedure
EXEC getSoccerLists;
For lower versions(Oracle 11g) , you may use a print command to display the cursor's o/p passing ref cursor as out parameter.
CREATE OR REPLACE PROCEDURE getSoccerLists (x OUT SYS_REFCURSOR)
AS
BEGIN
OPEN x FOR SELECT DISTINCT L.* FROM LABALES L
JOIN MATCHES M ON ( 1=1 ) -- join condition
WHERE M.LIST LIKE '%ENG'
ORDER BY L.ID;
END;
/
Then, in SQL* Plus or running as script in SQL developer and Toad, you may get the results using this.
VARIABLE r REFCURSOR;
EXEC getSoccerLists (:r);
PRINT r;
Another option is to use TABLE function by defining a collection of the record type of the result within a package.
Refer Create an Oracle function that returns a table
I guess this questions is a repetition of the your previously asked question, where you wanted to get all the columns of tables but into separate column. I already answered in stating this you cannot do if you call your function via a SELECT statement. If you call your function in a Anoymous block you can display it in separate columns.
Here Oracle function returning all columns from tables
Alternatively, you can get the results separated by a comma(,) or pipe (|) as below:
CREATE OR REPLACE
FUNCTION getSoccerLists
RETURN VARCHAR2
IS
list VARCHAR2(2000);
BEGIN
SELECT col1
||','
||col2
||','
||col2
INTO LIST
FROM SOCCER_PREMATCH_LISTS L ,
SOCCER_PREMATCH_MATCHES M
WHERE M.LIST LIKE '%' || (L.SUB_LIST) || '%'
AND (TO_TIMESTAMP((M.M_DATE || ' ' || M.M_TIME), 'DD.MM.YYYY HH24:MI') >
(SELECT SYSTIMESTAMP AT TIME ZONE 'CET' FROM DUAL
))
ORDER BY L.ID");
Return list;
End;
Note here if the column size increased 2000 chars then again you will lose the data.
Edit:
From your comments
I want it to return a table set of results.
You then need to create a table of varchar and then return it from the function. See below:
CREATE TYPE var IS TABLE OF VARCHAR2(2000);
/
CREATE OR REPLACE
FUNCTION getSoccerLists
RETURN var
IS
--Initialization
list VAR :=var();
BEGIN
SELECT NSO ||',' ||NAME BULK COLLECT INTO LIST FROM TEST;
RETURN list;
END;
Execution:
select * from table(getSoccerLists);
Note: Here in the function i have used a table called test and its column. You replace your table with its columnname.
Edit 2:
--Create a object with columns same as your select statement
CREATE TYPE v_var IS OBJECT
(
col1 NUMBER,
col2 VARCHAR2(10)
)
/
--Create a table of your object
CREATE OR REPLACE TYPE var IS TABLE OF v_var;
/
CREATE OR REPLACE FUNCTION getSoccerLists
RETURN var
IS
--Initialization
list VAR :=var();
BEGIN
--You above object should have same columns with same data type as you are selecting here
SELECT v_var( NSO ,NAME) BULK COLLECT INTO LIST FROM TEST;
RETURN list;
END;
Execution:
select * from table(getSoccerLists);
This is not an answer on how to build a function for this, as I'd recommend to make this a view instead:
CREATE OR REPLACE VIEW view_soccer_list AS
SELECT *
FROM soccer_prematch_lists l
WHERE EXISTS
(
SELECT *
FROM soccer_prematch_matches m
WHERE m.list LIKE '%' || (l.sub_list) || '%'
AND TO_TIMESTAMP((m.m_date || ' ' || m.m_time), 'DD.MM.YYYY HH24:MI') >
(SELECT SYSTIMESTAMP AT TIME ZONE 'CET' FROM DUAL)
);
Then call it in a query:
SELECT * FROM view_soccer_list ORDER BY id;
(It makes no sense to put an ORDER BY clause in a view, because you access the view like a table, and table data is considered unordered, so you could not rely on that order. The same is true for a pipelined function youd access with FROM TABLE (getSoccerLists). Always put the ORDER BY clause in your final queries instead.)

INSERT with dynamic column names

I have column names stored in variable colls, next I execute code:
DO $$
DECLARE
v_name text := quote_ident('colls');
BEGIN
EXECUTE 'insert into table1 select '|| colls ||' from table2 ';
-- EXECUTE 'insert into table1 select '|| v_name ||' from table2 ';
END$$;
I have got error: column "colls" does not exist. Program used colls as name not as variable. What am I doing wrong?
I have found similar example in documentation:
https://www.postgresql.org/docs/8.1/static/plpgsql-statements.html#PLPGSQL-STATEMENTS-EXECUTING-DYN
I have column names stored in variable colls
No, you don't. You have a variable v_name - which holds a single word: 'colls'. About variables in SQL:
User defined variables in PostgreSQL
Read the chapters Identifiers and Key Words and Constants in the manual.
And if you had multiple column names in a single variable, you could not use quote_ident() like that. It would escape the whole string as a single identifier.
I guess the basic misunderstanding is this: 'colls' is a string constant, not a variable. There are no other variables in a DO statement than the ones you declare in the DECLARE section. You might be looking for a function that takes a variable number of column names as parameter(s) ...
CREATE OR REPLACE FUNCTION f_insert_these_columns(VARIADIC _cols text[])
RETURNS void AS
$func$
BEGIN
EXECUTE (
SELECT 'INSERT INTO table1 SELECT '
|| string_agg(quote_ident(col), ', ')
|| ' FROM table2'
FROM unnest(_cols) col
);
END
$func$ LANGUAGE plpgsql;
Call:
SELECT f_insert_these_columns('abd', 'NeW Deal'); -- column names case sensitive!
SELECT f_insert_these_columns(VARIADIC '{abd, NeW Deal}'); -- column names case sensitive!
Note how I unnest the array of column names and escape them one by one.
A VARIADIC parameter should be perfect for your use case. You can either pass a list of column names or an array.
Either way, be vary of SQL injection.
Related, with more explanation:
Pass multiple values in single parameter
Table name as a PostgreSQL function parameter

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;

Firebird pass input parameter to gen_id function

I want to create stored procedure to get the current id for specific tables i have many tables
so i don't want to create sp for each one,
I'm trying this way but i fail
create procedure
sp_get_id(mytable varchar(128)) returns(id integer)
as
begin
select gen_id(:mytable, 0) from rdb$database into :id;
suspend;
end
I wonder if there is a way to pass the input param to the gen_id or i must create different sp for each table..
thanks in advance
If you keep a naming convention so that the generator name can be derived from the table name you could use something similar to this:
SET TERM ^;
CREATE OR ALTER PROCEDURE SP_GET_ID (
TABLE_NAME VARCHAR(128))
RETURNS (
ID INTEGER)
AS
BEGIN
EXECUTE STATEMENT 'SELECT GEN_ID(GEN_' || :TABLE_NAME || ', 0) FROM RDB$DATABASE' INTO :ID;
END^
SET TERM ;^