PLSQL - IN clause using variable with multiple values - sql

I am looping a statement and extracting the IDs and putting it into a variable:
declare
l_id NUMBER;
BEGIN
FOR i IN 1..l_row_count
loop
l_id := to_number(apex_json.get_varchar2(p_path => 'rows[%d].id',
p0 => i, p_values => l_values
)
);
end loop:
I am trying to get all the ids and then run a select statement where this list of IDs will be used inside of a where statement. So the full code would look like this:
declare
l_id NUMBER;
l_id_list [SOME_TYPE_NOT_SURE];
BEGIN
FOR i IN 1..l_row_count
loop
l_id := to_number(apex_json.get_varchar2(p_path => 'rows[%d].id',
p0 => i, p_values => l_values
)
);
//somehow add l_id to the l_id_list
end loop:
SELECT * FROM mytable WHERE someid IN (l_id_list);
END;
I haven't been able to get it to work. I am not an expert in PLSQL, but I tried using arrays or trying to concat the l_id_list as a string, but I am unable to get it work. Basically all I am trying to do is create a list of all the IDs, then run a select statement to see if these IDs exist in this other table.

Declare the type globally:
CREATE TYPE number_list IS TABLE OF NUMBER;
Then you can initialise the list and then, in each iteration of the loop, EXTEND the list and assign the value and, finally, use the MEMBER OF operator or IN with a subquery and table collection expression:
DECLARE
l_id_list NUMBER_LIST := NUMBER_LIST();
BEGIN
FOR i IN 1..l_row_count
LOOP
l_id_list.EXTEND;
l_id_list(l_id_list.COUNT) := to_number(
apex_json.get_varchar2(
p_path => 'rows[%d].id',
p0 => i,
p_values => l_values
)
);
END LOOP;
SELECT *
-- BULK COLLECT INTO ...
FROM mytable
WHERE someid MEMBER OF l_id_list;
-- or
SELECT *
-- BULK COLLECT INTO ...
FROM mytable
WHERE someid IN (SELECT COLUMN_VALUE FROM TABLE(l_id_list));
END;

In your case l_id_list's type must be declared globally and as collection-type. Locally defined types cannot be used as collections, so cannot be used inside in-clause.
declare
l_id NUMBER;
l_id_list [SOME_GLOBAL_COLLECTION_TYPE];
BEGIN
FOR i IN 1..l_row_count
loop
l_id := to_number(apex_json.get_varchar2(p_path => 'rows[%d].id',
p0 => i, p_values => l_values
)
);
//somehow add l_id to the l_id_list
end loop:
SELECT * FROM mytable WHERE someid IN (select * from table(l_id_list));
END;

Related

How can I get column names from CTE(common table expression) in Oracle?

Is there a function that gives the names a common table expression(~ a subtable in a with statement).
I would like a function f like that
with a as (select 1 a from dual) select f(a.*) from a
column1=a
Or perhaps are the CTE saved somewhere?
I could do something like that.
Is there a equivalent of USER_TAB_COLUMNS for the CTE.
You may use DBMS_SQL package to parse the statement and get the column names:
declare
cur integer;
cols dbms_sql.desc_tab;
num_cols integer;
begin
cur := dbms_sql.open_cursor();
/*Prase statement*/
dbms_sql.parse(
c => cur,
statement => q'{
select dual.*, 'something' as qwe
from dual
}',
language_flag => dbms_sql.native
);
/*Get cols*/
dbms_sql.describe_columns(
c => cur,
col_cnt => num_cols,
desc_t => cols
);
for i in 1..num_cols loop
dbms_output.put('Col index: ' || i);
dbms_output.put_line(' Col name: ' || cols(i).col_name);
end loop;
dbms_sql.close_cursor(cur);
end;
/
dbms_output:
Col index: 1 Col name: DUMMY
Col index: 2 Col name: QWE
Or with a local function declaration, if you want it to be selectable:
with function get_cols(
p_stmt in clob
) return sys.odcivarchar2list
as
pragma autonomous_transaction;
ret sys.odcivarchar2list := sys.odcivarchar2list();
cur integer;
cols dbms_sql.desc_tab;
num_cols integer;
begin
cur := dbms_sql.open_cursor();
/*Prase statement*/
dbms_sql.parse(
c => cur,
statement => p_stmt,
language_flag => dbms_sql.native
);
/*Get cols*/
dbms_sql.describe_columns(
c => cur,
col_cnt => num_cols,
desc_t => cols
);
for i in 1..num_cols loop
ret.extend();
ret(i) := cols(i).col_name;
end loop;
dbms_sql.close_cursor(cur);
return ret;
end;
select column_value as col_names
from get_cols(p_stmt => q'{select dual.*, 'something' as qwe from dual}')
| COL_NAMES |
| :-------- |
| DUMMY |
| QWE |
db<>fiddle here
No. A CTE is part of a DML (data modification language) statement.
You wrote it, you should know what columns are in it!
If you need to know the column list of a completed SELECT statement, SELECT ... INTO a temporary table - the column list will then be visible in USER_TAB_COLUMNS.
with a CTE it is not possible. You can write your own function to parse your sql statement. Maybe in your function you can create table with 1=2 condition to get just column names then drop it in your function.
Otherwise;
you can create your table :
create table your_table
as
with a as (select 1 a from dual) select a.* from a where 1=2
then find like this:
SELECT table_name, column_name, data_type, data_length
FROM USER_TAB_COLUMNS
WHERE table_name = 'your_table'

Use of variable in result_scan() snowflake stored procedure

I am trying to store the result of a last_query_id() as a variable and then later on in the stored procedure I am trying to use it in a table(result_scan()). I am coming across a number of errors when doing this, however I'm sure it's my level of knowledge of how snowflake works that s the problem. My code currently looks like this:
Declare query_id as varchar;
...
a number of queries;
...
query_id := (select last_query_id());
...
Some more queries
...
let c1 CURSOR for select * from table(result_scan(:query_id));
This is giving me an error saying result_scan() requires a string. I have tried using CAST to convert it to a string however this is not working either.
Thanks!
The assignment is possible:
BEGIN
LET query_id varchar;
SELECT 1 AS c;
query_id := (select last_query_id());
RETURN :query_id;
END;
The RESULT_SCAN function requires string literal so the following will not work:
DECLARE
query_id varchar;
r INT;
BEGIN
SELECT 1 AS c;
query_id := (select last_query_id());
LET c1 CURSOR for select * from table(result_scan(?));
OPEN C1 USING(:query_id);
FETCH c1 INTO r;
RETURN r;
END;
A workaround could be usage of EXECUTE IMMEDIATE and RESULTSET:
DECLARE
rs RESULTSET;
query_id VARCHAR;
BEGIN
SELECT 1 AS col1, 'a' AS col2;
query_id := (SELECT last_query_id());
let sql VARCHAR := 'select * from table(result_scan(''' || query_id || '''))';
rs := (EXECUTE IMMEDIATE :sql);
RETURN TABLE(rs);
END;
Output:
the syntax for assigning the result of a SELECT to a variable is
SELECT COL1 ... INTO variable
It's documented here

How include array (or similar) into an in-clause SQL statement?

I am new to oracle and PL SQL. Currently I struggle with the handling of arrays and "similar things" like i.e. collection. I am trying to build a procedure like:
procedure insert_by_array( my_array some_array_type)
begin
insert into table1 (some_column)
select some_column
from table2
where column2 in my_array
;
end;
However I could not make I did try some array types but I did not find the right one. The entries of the type must be varchar2 - a part of this criterium I am open to any array type. I.e when my array_type is
type array_of_strings is varray(100) of varchar2(40);
My error would be: "local collection types are not alowed in sql statements"
I am using Oracle Database 12c Enterprise Edition Release 12.1.0.2.0.
So at the end, this worked:
create type table_of_strings IS TABLE OF VARCHAR2(64); --define it global;
declare
my_table table_of_strings;
begin
my_table := table_of_strings('aaa', 'bb','c');
insert into table1 (some_column)
select some_column
from table2
where column2 in (select * from table(my_table))
;
end;
First of all, you have to define sql level collection. (varray is not an option)
create type array_of_strings as table of varchar2(40);
Now you can use table or "member of" approach
declare
c array_of_strings := new array_of_strings();
begin
c.extend();
c(c.count) := 'A';
c.extend();
c(c.count) := 'B';
for rec in (select * from dual where 'A' member of c ) loop
dbms_output.put_line('Option with memeber of ');
end loop;
for rec in (select * from dual where 'A' in (select * from table(c))) loop
dbms_output.put_line('Option with table');
end loop;
end;
As error says, local collections, defined in procedure, function, code block, cannot be used in queries, at least in Oracle 11 which I use.
This does not work (PLS-00642):
declare
type strings is table of varchar2(5);
v_i strings := strings('A', 'X', 'Q');
v_o strings;
begin
select * bulk collect into v_o from dual where dummy in (select * from table(v_i));
end;
So... either use type defined at schema level (your own or predefined sys.odcivarchar2list):
declare
type strings is table of varchar2(5);
v_i strings := strings('A', 'X', 'Q');
v_o strings;
v_so sys.odcivarchar2list := sys.odcivarchar2list();
begin
v_so.extend(v_i.count);
for i in 1..v_i.count loop
v_so(i) := v_i(i);
end loop;
select * bulk collect into v_o from dual where dummy in (select * from table(v_so));
end;
... either use dynamic sql:
declare
type strings is table of varchar2(5);
v_i strings := strings('A', 'X', 'Q');
v_o strings;
v_str varchar2(4000);
begin
for i in 1..v_i.count loop
v_str := v_str || case when i > 1 then ', ' end || ''''||v_i(i)||'''';
end loop;
execute immediate 'select * from dual where dummy in ('||v_str||')' bulk collect into v_o ;
end;
... either use loop (probably slowest):
declare
type strings is table of varchar2(5);
v_i strings := strings('A', 'X', 'Q');
v_o strings := strings();
begin
for rec in (select * from dual) loop
if rec.dummy member of v_i then
v_o.extend();
v_o(v_o.count) := rec.dummy;
end if;
end loop;
end;

Alias for a table of type?

I have the following definition for a table type
create table number_table as table of number;
And I'd like to use it like in the following example.
declare
l_myTable number_table := number_table(1, 2, 3);
begin
for i in (select * from l_myTable) loop
dbms_output.put_line(i.???); -- how do I reference the numbers here?
end loop;
end;
Forgive my code being somewhat pointless, how would I reference that i.??? though to get the number out of the iterator?
Just slightly change your query to
select rownum, column_value from l_myTable
and then use dbms_output.put_line(i.rownum); as an index and dbms_output.put_line(i.column_value ); as a value
create type number_table as table of number;
/
You do not need to use a Cursor FOR LOOP as you can just iterate over the collection:
declare
l_myTable number_table := number_table(3, 2, 1);
begin
for i in 1 .. l_myTable.COUNT loop
dbms_output.put_line( l_myTable(i) );
end loop;
end;
/
However, if there is some reason why you need to use a cursor then you can use the ROWNUM pseudocolumn to get index within the collection and the COLUMN_VALUE pseudocolumn to get the value at that index in the collection using the query:
SELECT ROWNUM, COLUMN_VALUE FROM TABLE( l_myTable )
Like this:
declare
l_myTable number_table := number_table(3, 2, 1);
begin
for i in ( SELECT ROWNUM, COLUMN_VALUE FROM TABLE( l_myTable ) ) loop
dbms_output.put_line( i.rownum || ': ' || i.column_value ); -- how do I reference the numbers here?
end loop;
end;
/

I need to use variable with TABLE type in IN operator

I need help with PL/SQL. How can i use variable with TABLE data type (in my case this variable involve 3 VARCHAR2 elements) with IN operator, without use access by index?
Example
select field1
from dual
where field1 in (myTableVariable);
myTableVariable must returning from function.
Not finished function:
declare
v_string varchar2(100);
v_string2 varchar2(100);
TYPE V_ARRAY IS TABLE OF VARCHAR2(10)
INDEX BY BINARY_INTEGER;
result V_ARRAY;
n number;
begin
n := 1;
v_string := '13,15,02';
while v_string != ' ' loop
select regexp_substr(v_string, '^[a-z0-9]*', 1),
regexp_replace(v_string, '^[a-z0-9]*(,|$)', '')
into v_string2, v_string
from dual;
result(n) := v_string2;
n := n + 1;
dbms_output.put_line(v_string2);
end loop;
return result;
end;
First:
Declare your table data type on the schema level (i.e. not in a package).
Then:
select field1
from dual
where feld1 in (select column_value from table(myTableVariable));
Enjoy!