ORACLE PL/SQL : Join cursor in subselect - sql

Is is possible to use cursor as a part of statement. Lets say i have something like this (pseudo code)
CURSOR SOURCE IS
SELECT
ID,
FLAG
FROM TABLE_OLD_A
WHERE 1=1;
TYPE t_new_a IS TABLE OF TABLE_NEW_A%ROWTYPE INDEX BY BINARY_INTEGER;
var_t_new_a t_new_a;
OPEN SOURCE;
FETCH SOURCE BULK COLLECT INTO var_t_new_a;
CLOSE SOURCE;
so far it works.
Now I would like to check if the migration from old to new is ok
WITH OLD_SOURCE AS
(
SELECT
ID,
FLAG
FROM SOURCE -> can i use cursor here in order not to copy the same select from cursor ?
)
SELECT COUNT(*) FROM TABLE_NEW_A
WHERE EXISTS
(
SELECT *
FROM OLD_SOURCE
WHERE 1=1
AND TABLE_NEW_A.ID = OLD_SOURCE.ID
AND TABLE_NEW_A.FLAG = OLD_SOURCE.FLAG
);

You can use the XML and TABLE function approach as follows to use the cursor in the query as a table as follows:
SQL> SET SERVEROUT ON
SQL> DECLARE
2 CUR SYS_REFCURSOR; -- DECLARING THE CURSOR
3 L_COUNT NUMBER;
4 BEGIN
5 --OPENING THE CURSOR
6 OPEN CUR FOR
7 SELECT 1 AS A, 5 AS B FROM DUAL UNION ALL
8 SELECT 3 AS A, 12 AS B FROM DUAL;
9
10 -- USING THE CURSOR AS A TABLE
11 SELECT MAX(B - A)
12 INTO L_COUNT
13 -- FOLLOWING CODE CONVERTS THE CURSOR TO TABLE
14 FROM (
15 SELECT EXTRACTVALUE(VALUE(SRC),'ROW/A') A,
16 EXTRACTVALUE(VALUE(SRC),'ROW/B') B
17 FROM TABLE ( XMLSEQUENCE(
18 XMLTYPE(CUR).EXTRACT('ROWSET/ROW')
19 ) ) SRC
20 ) CUR_TABLE;
21
22 DBMS_OUTPUT.PUT_LINE('Result: ' || L_COUNT);
23 END;
24 /
Result: 9
PL/SQL procedure successfully completed.
SQL>

Related

How to dynamically construct table name

I would like to construct a query where a table name is based off of another table's column mod 12. For example:
SELECT *
FROM table_b_XX
where XX here is determined by table_a.column_a % 12.
Presuming you have such a tables:
SQL> create table table_a as
2 select 1212 as column_a from dual;
Table created.
As the following result returns 0, we need table_b_00 so I'll create it:
SQL> select mod(1212, 12) from dual;
MOD(1212,12)
------------
0
SQL> create table table_b_00 as select 'table 00' name from dual;
Table created.
SQL> create table table_b_01 as select 'table 01' name from dual;
Table created.
Now, create a function which returns ref cursor; it selects rows from a table whose name is designed by the help of the table_a contents:
SQL> create or replace function f_test return sys_refcursor
2 is
3 l_str varchar2(200);
4 rc sys_refcursor;
5 begin
6 select 'select * from table_b_' || lpad(mod(a.column_a, 12), 2, '0')
7 into l_str
8 from table_a a;
9
10 open rc for l_str;
11 return rc;
12 end f_test;
13 /
Function created.
Let's try it:
SQL> select f_test from dual;
F_TEST
--------------------
CURSOR STATEMENT : 1
CURSOR STATEMENT : 1
NAME
--------
table 00
Right; that's contents of table_b_00.
Consider the following meta code:
DECLARE
n VARCHAR2(32767);
r VARCHAR2(32767);
BEGIN
SELECT column_a INTO name FROM table_a;
EXECUTE IMMEDIATE 'SELECT r FROM table_b_'||n INTO r;
END;
/

How to use a dynamic variable created using INTO within a new query using IN clause under where condition in PL/SQL

DECLARE
l_filter varchar2(100);
BEGIN
SELECT filter INTO l_filter
FROM dashboard
WHERE id=TARGET_ID_1;
I get l_filter as ('016','018','011','014') from this. Now i want to use this l_filter values within IN clause in the query below dynamically.
select a,b from grid
where filter_value in l_filter;
How can I do this in PL/SQL?
You could use regular expressions:
select g.a, g.b
from grid g
where exists (select 1
from dashboard d.
where d.id = TARGET_ID_1 and
regexp_like(d.filter_value, '^(' || replace(d.filter, '''', ''), ',', '|') || ')$')
);
This is not particularly efficient, but it does not require any PL/SQL or dynamic SQL.
That said, you might want to store your "filters" as rows in a table so you can just join things together, dispensing with the complication.
Use LIKE:
DECLARE
l_filter varchar2(100);
cur SYS_REFCURSOR;
a GRID.A%TYPE;
b GRID.B%TYPE;
BEGIN
SELECT filter
INTO l_filter
FROM dashboard
WHERE id = TARGET_ID_1;
OPEN cur FOR
SELECT a,b
FROM grid
WHERE ','||SUBSTR(l_filter,2,LENGTH(l_filter)-2)||',' LIKE '%,'''||filter_value||''',%';
LOOP
FETCH cur INTO a,b;
EXIT WHEN cur%NOTFOUND;
DBMS_OUTPUT.PUT_LINE(a || ', ' || b);
END LOOP;
CLOSE cur;
END;
/
db<>fiddle here
Here's how I understood the question.
Sample data first:
SQL> select * from dashboard;
ID FILTER
---------- -----------------------
1 '016','018','011','014'
2 '111', '222'
SQL> select * from grid;
A B FIL
---------- ---------- ---
100 200 016
101 201 011
200 400 xxx
SQL>
Function that returns refcursor; the key (in this example) is to split filter values into rows so that you could use them as a subquery (lines #15 - 18):
SQL> create or replace function f_get (target_id_1 in dashboard.id%type)
2 return sys_refcursor
3 is
4 l_filter varchar2(100);
5 rc sys_refcursor;
6 begin
7 select filter
8 into l_filter
9 from dashboard
10 where id = target_id_1;
11
12 open rc for
13 select g.a, g.b
14 from grid g
15 where g.filter_value in (select regexp_substr(replace(l_filter, chr(39), ''), '[^,]+', 1, level)
16 from dual
17 connect by level <= regexp_count(l_filter, ',') + 1
18 );
19
20 return rc;
21 end;
22 /
Function created.
Testing:
SQL> select f_get(1) from dual;
F_GET(1)
--------------------
CURSOR STATEMENT : 1
CURSOR STATEMENT : 1
A B
---------- ----------
100 200
101 201
SQL>

How do I insert data from a table to the XML table

When I run this code I didn't get the right data from the standard table
create table COUNTRY_XML(
C_X sys.xmltype);
begin
for cursor in
(select country, continental, population2019 from countries)
loop
insert into country_xml values(
sys.xmltype.createXML(
'<ac_x createdby="Guangzhe">
<country_info>
<Country>mycursor.country</Country>
<Continental>mycursor.continental</Continental>
<Population2019>mycursor.population2019</Population2019>
</country_info>
</ac_x>'));
end loop;
end;
select c.c_x.extract('/').getstringval() from country_xml c
There results are as follow and they are all the same things for each row.
"<ac_x createdby="Guangzhe">
<country_info>
<Country>mycursor.country</Country>
<Continental>mycursor.continental</Continental>
<Population2019>mycursor.population2019</Population2019>
</country_info>
</ac_x>"
You are not doing concatnation properly.
You need to use || (concatanation operator) as following
Table creation
SQL> CREATE TABLE COUNTRY_XML (
2 C_X SYS.XMLTYPE
3 );
Table created.
Solution you need:
SQL>
SQL> begin
2 for mycursor in -- changed the name
3 (select 'INDIA' as country, 'ASIA' as continental, '130B' as population2019 from dual)
4 --(select country, continental, population2019 from countries)
5 loop
6 insert into country_xml values(
7 sys.xmltype.createXML(
8 '<ac_x createdby="Guangzhe">
9 <country_info>
10 <Country>' || mycursor.country || '</Country>
11 <Continental>' || mycursor.continental|| '</Continental>
12 <Population2019>' || mycursor.population2019|| '</Population2019>
13 </country_info>
14 </ac_x>'));
15 end loop;
16 end;
17 /
PL/SQL procedure successfully completed.
Output
SQL> SELECT C.C_X.EXTRACT('/').GETSTRINGVAL()
2 FROM COUNTRY_XML C
3 ;
C.C_X.EXTRACT('/').GETSTRINGVAL()
--------------------------------------------------------------------------------
<ac_x createdby="Guangzhe">
<country_info>
<Country>INDIA</Country>
<Continental>ASIA</Continental>
<Population2019>130B</Population2019>
</country_info>
</ac_x>
SQL>
Cheers!!

oracle choose which columns to show

i' trying to create a SELECT in ORACLE .
i'm selecting from a table 3 columns
and i want to do a test (result of an other select)
if it's true show all columns
if false only show two.
create table t1(a int ,b int , c int) ;
select a , case when (1=1) then (b ,c)
else (b) end;
from t1 ;
It's not quite clear what you're trying to do.
If you're just interested in the result of this query, you can't do this. You can't have a query that returns an unknown number of columns. You could have three column and one be null unless your condition is met, like:
SELECT a, CASE WHEN ( condition ) THEN b ELSE NULL END AS b, c
FROM t1
If your goal is to actually create a table (but I would suggest strongly against doing table creation like this), you can use EXECUTE IMMEDIATE string, where string is a DDL command:
DECLARE
ddl VARCHAR2(4000);
BEGIN
IF (condition) THEN
ddl := 'CREATE TABLE t1 (a NUMBER, b NUMBER, c NUMBER )';
ELSE
ddl := 'CREATE TABLE t1 (a NUMBER, b NUMBER )';
END IF;
EXECUTE IMMEDIATE ddl;
END;
plsql with execute immediate is good decigin in your case. But if you want only data you may try like this, may be it's help you:
SELECT a,
CASE
WHEN (condition) THEN
b
ELSE
nvl(b, '') || ';' || nvl(c, '')
END AS NEW_COL
FROM t1
i.e. using pl/sql to open a cursor that selects columns dependant on a given input.
declare
v_select_all_cols boolean := true; --set as applicable.
v_rc sys_refcursor;
begin
if (v_select_all_cols)
then
open v_rc for select a,b,c from t1;
else
open v_rc for select a,b from t1;
end if;
-- now you can return the resultset v_rc to the caller
end;
/
e.g a quick test with sqlplus (ill use a var to print the cursor instead of a pl/sql variable)
SQL> var rc refcursor;
SQL> declare
2 v_select_all_cols boolean := true; --set as applicable.
3 begin
4
5 if (v_select_all_cols)
6 then
7 open :rc for select a,b,c from t1;
8 else
9 open :rc for select a,b from t1;
10 end if;
11 end;
12 /
PL/SQL procedure successfully completed.
SQL> print rc
A B C
---------- ---------- ----------
1 2 3
SQL> declare
2 v_select_all_cols boolean := false;
3 begin
4
5 if (v_select_all_cols)
6 then
7 open :rc for select a,b,c from t1;
8 else
9 open :rc for select a,b from t1;
10 end if;
11 end;
12 /
PL/SQL procedure successfully completed.
SQL> print rc
A B
---------- ----------
1 2

Oracle SQl Populating a cursor in a procedure from a sql statement saved in a table field

We want to populate a cursor in a procedure that is populated from a select statement in a table.
We created a table named stored_sql_statments with 2 columns, Created_date & Sql_statement.
In that table we will insert a select statement that selects other data from the database based on the clients needs.
Example:
insert into stored_sql_statments
( Created_date , Sql_statement)
values('2/1/2011', 'Select Client_idn , something_neat from cool_table where animal = 'dog' ')
Then in the procedure we have a bunch of code that does what it needs to do, which will never change, but the select statement we have in the cursor changes periodically. We always need to return 2 fields but the rest of the select statement changes.
So now we need to populate the cursor in the procedure with what the select statement coming form the table.
If it was returning only 1 row we have:
declare
x varchar2(600);
rec1 number(10);
rec2 varchar2(15);
begin
execute immediate select Sql_statement into x from stored_sql_statments where created_date = '2/1/2011';
execute immediate x into rec1, rec2;
...
This works, but we don't need it to go into 2 variables we need it to go into a cursor. The real select statement (the above code is just a simple example of what we need to do) is bringing back thousands of records so we need to use a cursor.
Hope this all makes sense
So if anyone knows how to do this, it would be appreciated.
Are you trying to dynamically populate a ref cursor? if so that isn't difficult to do:
set serveroutput on
declare
sql1 varchar2(500);
sql2 varchar2(500);
procedure runProcess(sqlstatement IN varchar2)
AS
refcrs sys_refcursor;
DTE DATE;
LEVELB NUMBER;
BEGIN
dbms_output.put_line(sqlstatement);
open refcrs for
sqlstatement; -- use 'using' to bind those variables
loop
fetch refcrs into DTE, LEVELB;
exit when refcrs%notfound;
dbms_output.put_line(TO_CHAR(DTE,'MMDDYYYY') || '/' || LEVELB);
end loop;
CLOSE REFCRS;
END runProcess;
begin
sql1 := 'select (sysdate - level) a, level b from dual connect by level < 5';
sql2:= 'select (sysdate + level) a, -level b from dual connect by level < 5';
runProcess(SQL1);
runProcess(SQL2);
end ;
/**
select (sysdate - level) a, level b from dual connect by level < 5
02102011/1
02092011/2
02082011/3
02072011/4
select (sysdate + level) a, -level b from dual connect by level < 5
02122011/-1
02132011/-2
02142011/-3
02152011/-4
**/
As you can see, I am dynamically executing two different select statements in the same procedure and outputting their results.
SQL> drop table stack_overflow;
Table dropped.
SQL> create table stack_overflow (created_date date constraint stack_overflow_pk primary key
2 , sql_statement varchar2(4000) not null)
3 /
Table created.
SQL> drop table source_data;
Table dropped.
SQL> create table source_data (vc varchar2(10) null
2 , n number);
Table created.
SQL> insert into source_data values ('a', 100);
1 row created.
SQL> insert into source_data values ('a', 0);
1 row created.
SQL> insert into source_data values ('b', 50);
1 row created.
SQL> insert into source_data values ('c', null);
1 row created.
SQL> insert into stack_overflow values (sysdate - 3/24, 'select vc, sum(n)
2 from source_data
3 group by vc
4 order by vc asc');
1 row created.
SQL> insert into stack_overflow values (sysdate - 2/24 , 'select vc, avg(n)
2 from source_data
3 group by vc
4 order by vc desc');
1 row created.
SQL> insert into stack_overflow values (sysdate - 1/24 , 'select vc, count(*)
2 from source_data
3 group by vc');
1 row created.
SQL> insert into stack_overflow values (sysdate, 'select vc, count(n)
2 from source_data
3 group by vc');
1 row created.
SQL> commit;
Commit complete.
SQL> declare
2 type stack_overflow_type is record (col_1 varchar2(10), col_2 number);
3 type stack_overflow_cur_type is ref cursor return stack_overflow_type;
4 cursor sql_statement_cur is select sql_statement
5 from stack_overflow
6 order by created_date;
7 --
8 function get_cursor_by_date (i_created_date in date) return stack_overflow_cur_type is
9 l_return_cur sys_refcursor; -- stack_overflow_cur_type;
10 l_sql_statement stack_overflow.sql_statement%TYPE;
11 begin
12 select sql_statement into l_sql_statement
13 from stack_overflow
14 where created_date = i_created_date;
15 --
16 open l_return_cur for l_sql_statement;
17 return l_return_cur;
18 end get_cursor_by_date;
19 --
20 procedure process_and_close_cursor (i_cursor in stack_overflow_cur_type) is
21 l_current_rec stack_overflow_type;
22 begin
23 loop
24 fetch i_cursor into l_current_rec;
25 exit when i_cursor%NOTFOUND;
26 dbms_output.put_line('col_1: ' || l_current_rec.col_1
27 || ' col_2: ' || to_number(l_current_rec.col_2));
28 end loop;
29 --
30 close i_cursor;
31 end;
32 --
33 begin
34 for l_row in (select created_date
35 from stack_overflow
36 order by created_date)
37 loop
38 dbms_output.put_line('Processing the SQL statement created on: '
39 || to_char(l_row.created_date, 'YYYY-MM-DD HH24:Mi:SS'));
40 --
41 process_and_close_cursor(get_cursor_by_date(l_row.created_date));
42 --
43 dbms_output.new_line;
44 end loop;
45 end;
46 /
Processing the SQL statement created on: 2011-02-11 10:01:16
col_1: a col_2: 100
col_1: b col_2: 50
col_1: c col_2:
Processing the SQL statement created on: 2011-02-11 11:01:16
col_1: c col_2:
col_1: b col_2: 50
col_1: a col_2: 50
Processing the SQL statement created on: 2011-02-11 12:01:17
col_1: a col_2: 2
col_1: b col_2: 1
col_1: c col_2: 1
Processing the SQL statement created on: 2011-02-11 13:01:17
col_1: a col_2: 2
col_1: b col_2: 1
col_1: c col_2: 0
PL/SQL procedure successfully completed.