To begin I am far from being an expert and I would like to have some guidance on the following subject :
I am trying to get a query result from several table without having the table names on the query but coming as a result of another table.
I have all the table names from 1 table :
Tables names from U5WOMA
What I would like to get is something as this as a result :
Union results from Tables stored in U5WOMA
select description, actif, code from U5WOCO
union
select description, actif, code from u5wowo
union
select description, actif, code from U5woeq;
Except that I would like this result without having to put the table names in the query but rather having them dynamically from the U5WOMA table since I will not know how many tables that I would have neither their names.
I did some research and I get the feeling that the response is in dynamic SQL but I am unable to find something even close to works.
I am using Oracle database.
If you have any suggestion, tutorial or advice that could help me to move to the right direction, it would be very appreciated.
Thank you,
Here's one option - create a function which composes a "long" select statement based on contents of the u5woma table and uses it as a source for the refcursor.
Sample data:
SQL> select * from u5woco;
DESCRIPTI ACTIF CODE
--------- ---------- ----------
MANAGER CLARK 7782
PRESIDENT KING 7839
CLERK MILLER 7934
SQL> select * from u5wowo;
DESCRIPTI ACTIF CODE
--------- ---------- ----------
CLERK SMITH 7369
MANAGER JONES 7566
ANALYST SCOTT 7788
CLERK ADAMS 7876
ANALYST FORD 7902
SQL> select * from u5woeq;
DESCRIPTI ACTIF CODE
--------- ---------- ----------
SALESMAN ALLEN 7499
SALESMAN WARD 7521
SALESMAN MARTIN 7654
MANAGER BLAKE 7698
SALESMAN TURNER 7844
CLERK JAMES 7900
6 rows selected.
The u5woma table that contains list of all tables involved in query:
SQL> select * from u5woma;
TABLE_
------
U5WOCO
U5WOWO
U5WOEQ
Function: in order for union to work, all select statements must return the same number of columns and they must match in datatype. Your example shows that all tables share the same column list (which simplifies the task):
SQL> create or replace function f_u5
2 return sys_refcursor
3 is
4 l_str varchar2(2000);
5 l_rc sys_refcursor;
6 begin
7 for cur_r in (select table_name from u5woma) loop
8 l_str := l_str || ' select description, actif, code from ' || cur_r.table_name
9 || ' union all';
10 end loop;
11
12 l_str := rtrim(l_str, ' union all');
13 open l_rc for l_str;
14 return l_rc;
15 end;
16 /
Function created.
SQL>
Testing:
SQL> select f_u5 from dual;
F_U5
--------------------
CURSOR STATEMENT : 1
CURSOR STATEMENT : 1
DESCRIPTI ACTIF CODE
--------- ---------- ----------
MANAGER CLARK 7782
PRESIDENT KING 7839
CLERK MILLER 7934
CLERK SMITH 7369
MANAGER JONES 7566
ANALYST SCOTT 7788
CLERK ADAMS 7876
ANALYST FORD 7902
SALESMAN ALLEN 7499
SALESMAN WARD 7521
SALESMAN MARTIN 7654
MANAGER BLAKE 7698
SALESMAN TURNER 7844
CLERK JAMES 7900
14 rows selected.
SQL>
There are other possibilities as well (for example, you could return a collection; or use a pipelined function; or ...), but the main principle remains the same.
thank you for your help, it is a very interesting way of managing it and it is very helpfull.
If I understand correctly all the results are stored in one variable. So as long as there is less than 2000 characters I will be fine but if there are more, it will be trunqued ?
When I call the funtion directly, I have the following result :
SQL>
{<DESCRIPTION=Commande 2,ACTIF=-,CODE=1001>,<DESCRIPTION=Commande 1,ACTIF=+,CODE=1000>,<DESCRIPTION=OT 1,ACTIF=+,CODE=1000>,<DESCRIPTION=OT 2,ACTIF=-,CODE=1001>,<DESCRIPTION=Actif 1,ACTIF=+,CODE=1000>,<DESCRIPTION=Actif 2,ACTIF=-,CODE=1001>,<DESCRIPTION=Actif 3,ACTIF=+,CODE=1003>,}
SQL>
Since the results are all in one line I have to split them in several lines to be able to use them.
I should be able to do it when I call the function f_u5 but is it possible to store the date directly on the function in order to get as many lines as I have results ?
Thank you,
I finally did it a little differently :
I first created a new table :
SQL>
create table SG_u5woma(description varchar2(80), actif varchar2(1), code varchar2(30));
SQL>
Then a stored procedure :
SQL>
create or replace procedure P_u5
as
begin
delete from SG_u5woma;
for cur_r in (select table_name from u5woma) loop
execute immediate 'insert into SG_u5woma (description, actif,code) select description, actif, code from ' || cur_r.table_name ;
end loop;
end;
/
SQL>
The I execute the procedure :
SQL>
exec P_u5();
SQL>
And the result is the data in the table :
SQL>
select * from SG_u5woma;
Commande 2 - 1001
Commande 2 + 1003
Commande 1 + 1000
OT 1 + 1000
OT 2 - 1001
Actif 1 + 1000
Actif 2 - 1001
Actif 3 + 1003
SQL>
Anyway, thank you for the help, it was very helpfull.
Related
I have following table structure.
CREATE TABLE "DADMIN"."DATATEMPLATES"
(
"ID" VARCHAR2(100 BYTE),
"QUERY" CLOB,
"ACTIVESTATUS" VARCHAR2(2 BYTE),
)
I'm storing query data like this SELECT CD.CIF , CD.CREDIT_ACCOUNT FROM CUTOMERDATA CD WHERE ID = :PARA_ID
Currently I'm executing it through the C# raw sql execution
Instead of that, I need to execute the table query through the stored procedure. How can I execute table stored query using SP? and return its data using cursor output?
Update:
This is my sample SP,
PROCEDURE Get_customer_data (p_Query_id IN VARCHAR2,
p_cursor OUT OUTPUTCURSOR)
IS
BEGIN
DECLARE
l_query CLOB;
BEGIN
SELECT query
INTO l_query
FROM querytemplates
WHERE id = p_Query_id ;
OPEN p_cursor FOR l_query;
END;
END;
But this makes error
ORA-06512: at line 1
01008. 00000 - "not all variables bound"
And my other problem is, the table stored query also excepting parameter called PARA_ID how can I pass that.
sample table stored query as follows,
SELECT CD.CIF , CD.CREDIT_ACCOUNT FROM CUTOMERDATA CD WHERE ID = :PARA_ID
One option is to return refcursor. Here's an example.
Sample data (that stores queries):
SQL> SELECT * FROM datatemplates;
ID QUERY A
---------- ------------------------------------------------------------ -
1 select deptno, dname from dept A
2 select d.dname, e.ename, e.job, e.sal from emp e join dept d A
on d.deptno = e.deptno
SQL>
Procedure - based on passed ID parameter - selects appropriate query and opens a refcursor based on that select statement:
SQL> CREATE OR REPLACE FUNCTION f_test (par_id IN NUMBER)
2 RETURN SYS_REFCURSOR
3 IS
4 l_query CLOB;
5 l_rc SYS_REFCURSOR;
6 BEGIN
7 SELECT query
8 INTO l_query
9 FROM datatemplates
10 WHERE id = par_id;
11
12 OPEN l_rc FOR l_query;
13
14 RETURN l_rc;
15 END;
16 /
Function created.
Testing:
SQL> SELECT f_test (1) FROM DUAL;
F_TEST(1)
--------------------
CURSOR STATEMENT : 1
CURSOR STATEMENT : 1
DEPTNO DNAME
---------- --------------
10 ACCOUNTING
20 RESEARCH
30 SALES
40 OPERATIONS
Some more testing:
SQL> SELECT f_test (2) FROM DUAL;
F_TEST(2)
--------------------
CURSOR STATEMENT : 1
CURSOR STATEMENT : 1
DNAME ENAME JOB SAL
-------------- ---------- --------- ----------
ACCOUNTING CLARK MANAGER 2450
ACCOUNTING KING PRESIDENT 5000
ACCOUNTING MILLER CLERK 1300
RESEARCH JONES MANAGER 2975
RESEARCH FORD ANALYST 3000
RESEARCH ADAMS CLERK 1100
RESEARCH SMITH CLERK 800
RESEARCH SCOTT ANALYST 3000
SALES WARD SALESMAN 1250
SALES TURNER SALESMAN 1500
SALES ALLEN SALESMAN 1600
SALES JAMES CLERK 950
SALES BLAKE MANAGER 2850
SALES MARTIN SALESMAN 1250
14 rows selected.
SQL>
Here are the instructions:
A company wants to allow customers to do product search by selecting a product name or
description, and then typing a search term. Using native dynamic SQL, create a
procedure name SEARCH_SP that returns a product name, description, and price base
on users’ search criteria. The procedure needs handle multiple rows being returned.
Here is the code I have so far.
CREATE OR REPLACE PROCEDURE search_sp (product_name IN VARCHAR2,
description IN VARCHAR2,
price_based IN NUMBER
)
AS
BEGIN
SELECT customer.product.name, customer.description, customer.price
FROM dbo.customer
WHERE customer.description = #SEARCH.customer.product.name = #SEARCH
END;
/
EXECUTE IMMEDIATE plsql_block
USING IN OUT new_product_name, new_description, new_price_based;
END;
/
I'm getting compilation errors and more. Any help or suggestions will be greatly appreciated.
Dynamic SQL? What for? To make your life more miserable than it should be? What's wrong with a straight SQL?
I'd suggest a function. Why? You're supposed to return the result. If it is a procedure, it has to have an OUT parameter. If that's so, then it's a function.
Here's how I'd do it; see how it works, use it (and improve) if you want. I don't think I'll get involved into anything dynamic here, sorry.
As I don't have your tables, I'll use Scott's sample schema. Here's data I'm interested in:
SQL> select d.dname, e.ename, e.job, e.sal
2 from dept d join emp e on e.deptno = d.deptno
3 order by d.dname, e.job, e.sal;
DNAME ENAME JOB SAL
-------------- ---------- --------- ----------
ACCOUNTING MILLER CLERK 1300
ACCOUNTING CLARK MANAGER 2450
ACCOUNTING KING PRESIDENT 5000
RESEARCH SCOTT ANALYST 3000
RESEARCH FORD ANALYST 3000
RESEARCH SMITH CLERK 800
RESEARCH ADAMS CLERK 1100
RESEARCH JONES MANAGER 2975
SALES JAMES CLERK 950
SALES BLAKE MANAGER 2850
SALES MARTIN SALESMAN 1250 --> for testing, I'll fetch SALESMEN
SALES WARD SALESMAN 1250 --> who earn 1250 USD (it's probably USD)
SALES TURNER SALESMAN 1500
SALES ALLEN SALESMAN 1600
14 rows selected.
Code that searches through it looks like this:
SQL> create or replace function search_sp
2 (par_dname in varchar2,
3 par_job in varchar2,
4 par_sal in number
5 )
6 return sys_refcursor
7 is
8 rc sys_refcursor;
9 begin
10 open rc for
11 select d.dname, e.ename, e.job, e.sal
12 from dept d join emp e on e.deptno = d.deptno
13 where (d.dname = par_dname or par_dname is null)
14 and (e.job = par_job or par_job is null)
15 and (e.sal = par_sal or par_sal is null);
16 return rc;
17 end;
18 /
Function created.
Let's test it:
SQL> select search_sp(null, 'SALESMAN', 1250) from dual;
SEARCH_SP(NULL,'SALE
--------------------
CURSOR STATEMENT : 1
CURSOR STATEMENT : 1
DNAME ENAME JOB SAL
-------------- ---------- --------- ----------
SALES WARD SALESMAN 1250
SALES MARTIN SALESMAN 1250
SQL>
Looks OK to me.
I want to do something like this:
create or replace procedure get_deleted_rows(result_set OUT SYS_REFCURSOR) is
begin
delete from table1
where start_date >= sysdate - 30 and start_date <= sysdate
returning * bulk collect into result_set;
end;
I saw that creating a type table of... and using bulk collect into a declared variable of this type was a way to go. But, this table I'm deleting has several columns and doing this would generate a lot of complexity for me to place it in production.
Is there a more simple way to return all the rows from a delete from a procedure?
... and doing this would generate a lot of complexity for me
I'm afraid that you'll just have to accept it.
Here's an example of how to do it.
Sample table; I'll delete rows for employees whose salaries are higher than 2000.
SQL> select * from test order by sal;
ENAME SAL STARS
---------- ---------- ----------
SMITH 800
JAMES 950
ADAMS 1100 *
WARD 1250 *
MARTIN 1250 *
TURNER 1500 *
ALLEN 1600 *
BLAKE 2850 ** --> delete Blake, Jones and Scott
JONES 2975 **
SCOTT 3000 ***
10 rows selected.
Let's start:
SQL> create or replace type t_row is object
2 (ename varchar2(10), sal number, stars varchar2(10));
3 /
Type created.
SQL> create or replace type t_tab as table of t_row;
2 /
Type created.
Procedure:
SQL> create or replace procedure p_test (par_rc out sys_refcursor)
2 is
3 l_tab t_tab;
4 begin
5 delete from test
6 where sal > 2000
7 returning t_row(ename, sal, stars) bulk collect into l_tab;
8
9 open par_Rc for select * from table (l_tab);
10 end;
11 /
Procedure created.
Testing:
SQL> var l_rc refcursor
SQL>
SQL> exec p_test(:l_rc);
PL/SQL procedure successfully completed.
SQL> print l_rc
ENAME SAL STARS
---------- ---------- ----------
JONES 2975 ** --> Deleted, as expected
BLAKE 2850 **
SCOTT 3000 ***
What's left?
SQL> select * from test order by sal;
ENAME SAL STARS
---------- ---------- ----------
SMITH 800
JAMES 950
ADAMS 1100 *
WARD 1250 *
MARTIN 1250 *
TURNER 1500 *
ALLEN 1600 *
7 rows selected.
SQL>
I'm trying to write a simple query where the requirement is to use a substitution variable that can be used to enter mutiple possible values for a column that's used for filtering the query.
The reqiurement is to produce the following query
select
CONTRACT,
ORDER_NO,
CUSTOMER_NO
from CUSTOMER_ORDER
where state='Picked'
and contract in ('ABC','DEF')
but the contract values will need to be entered during runtime by means of a substitution variable. I'm working with the limitation of only being able to write a static SQL Query ("select ..from..where..") and no dynamic code can be written inside pl sql blocks.
So, what I tried was the following,
select
CONTRACT,
ORDER_NO,
CUSTOMER_NO
from CUSTOMER_ORDER_JOIN
where contract in (select '''' || REPLACE('&CONTRACT',';',''',''') || '''' from dual)
When the prompt appears for the substitution, I enter ABC;DEF
But this doesn't seem to work. Although when I run the following separately,
select '''' || REPLACE('&CONTRACT',';',''',''') || '''' from dual
I get 'ABC','DEF' as the result.
Why is this not working? Is there a way to achieve my desired result ?
Thanks
One option is to split those values into rows and use them as a subquery.
Example based on Scott's EMP table:
SQL> select ename, job, sal from emp;
ENAME JOB SAL
---------- --------- ----------
SMITH CLERK 920
ALLEN SALESMAN 1600
WARD SALESMAN 1250
JONES MANAGER 2975
MARTIN SALESMAN 1250
BLAKE MANAGER 2850
CLARK MANAGER 2450
SCOTT ANALYST 3000
KING PRESIDENT 10000
TURNER SALESMAN 1500
ADAMS CLERK 1100
JAMES CLERK 950
FORD ANALYST 3000
MILLER CLERK 1300
14 rows selected.
SQL> select ename, job, sal
2 from emp
3 where ename in (select regexp_substr(replace(q'[&&par_ename]', chr(39), ''), '[^,]+', 1, level)
4 from dual
5 connect by level <= regexp_count(q'[&&par_ename]', ',') + 1
6 );
Enter value for par_ename: 'SMITH','ALLEN'
ENAME JOB SAL
---------- --------- ----------
SMITH CLERK 920
ALLEN SALESMAN 1600
SQL>
Alternatively, use sys.odcivarchar2list:
SQL> select ename, job, sal
2 from emp
3 where ename in (select column_value
4 from table(sys.odcivarchar2list(&par_ename))
5 );
Enter value for par_ename: 'SMITH','ALLEN'
ENAME JOB SAL
---------- --------- ----------
SMITH CLERK 920
ALLEN SALESMAN 1600
SQL>
It works without single quotes as well:
SQL> Select ename, job, sal
2 from emp
3 where ename in (select regexp_substr(replace(q'[&&par_ename]', chr(39), ''), '[^,]+', 1, level)
4 from dual
5 connect by level <= regexp_count(q'[&&par_ename]', ',') + 1
6 );
Enter value for par_ename: SMITH,ALLEN,KING
ENAME JOB SAL
---------- --------- ----------
SMITH CLERK 920
ALLEN SALESMAN 1600
KING PRESIDENT 10000
SQL>
I have a T-SQL stored procedure I'm trying to convert to PL-SQL.
Currently, this stored procedure executes a very long select query, immediately followed by a much shorter select query. It looks like so:
CREATE PROCEDURE proc_name
#userid int,
AS
select (about twenty fields)
from (about five tables)
where something = #userid
select (about five fields)
from (about three tables)
where something = #userid
My first thought when I saw this was to convert it to a view, since it's just doing a select statement. However, is this actually possible, given that two selects are being done? Could a stored procedure be used? I've experimented with that idea, but to my understanding I'd need to create two sys_refcursors, meaning the calling code would need to handle both query returns differently, which it's not having to do currently.
So question is: How can I convert that type of procedure, from T-SQL, to PL-SQL?
You cannot simply have a select query in PL/SQL. It will throw PLS-00428: an INTO clause is expected in this SELECT statement error.
To return multiple rows, you could use CURSOR. In your case, with multiple statements, you can have two REFCURSOR.
For example,
SQL> variable v_ref1 refcursor
SQL> variable v_ref2 refcursor
SQL>
SQL> DECLARE
2 v_ref1 sys_refcursor;
3 v_ref2 sys_refcursor;
4 BEGIN
5 OPEN :v_ref1 FOR SELECT empno, ename
6 FROM emp ORDER BY empno
7 FETCH FIRST 5 ROWS ONLY;
8 OPEN :v_ref2 FOR SELECT empno, ename
9 FROM emp ORDER BY empno DESC
10 FETCH FIRST 5 ROWS ONLY;
11 END;
12 /
PL/SQL procedure successfully completed.
SQL> print v_ref1
EMPNO ENAME
---------- ----------
7369 SMITH
7499 ALLEN
7521 WARD
7566 JONES
7654 MARTIN
SQL> print v_ref2
EMPNO ENAME
---------- ----------
7934 MILLER
7902 FORD
7900 JAMES
7876 ADAMS
7844 TURNER
SQL>
If you want to combine the resultset of your multiple SELECT statements, you could use UNION operator and have it in single REFCURSOR. Given that the column data types match and are in proper order. It is just an example,
SQL> variable v_ref refcursor
SQL>
SQL> DECLARE
2 v_ref sys_refcursor;
3 BEGIN
4 OPEN :v_ref FOR
5 SELECT empno, DEPTNO FROM emp WHERE ROWNUM <=5
6 UNION ALL
7 SELECT empno, DEPTNO FROM EMP WHERE ROWNUM <=5;
8 END;
9 /
PL/SQL procedure successfully completed.
SQL> print v_ref
EMPNO DEPTNO
---------- ----------
7369 20
7499 30
7521 30
7566 20
7654 30
7369 20
7499 30
7521 30
7566 20
7654 30
10 rows selected.
SQL>
You could make second select query have 20 columns as well by making the fields which are not there in the first select query NULL. But that depends if you are okay with that.