Getting multiple values from select statement into a variable of a procedure - database

I am trying to craete a procedure where the input to the procedure will be a string like 'a,b,c,d' and output will be the values from a table which match the comma separated value from in IN sting.
For that --
create table test (nm varchar2(10));
insert into test values ('a');
insert into test values ('b');
insert into test values ('c');
insert into test values ('d');
select * from test;
NM
------
a
b
c
d
Now i am trying to make a procedure where the IN parameter will be the values of NM column of TEST table in a comma separated string like 'a,b,c,d,x,w' there could be false values also.
So the procedure will return only the values which are matching with NM column of the TEST table for this i have created This procedure ---
create or replace procedure p_test (p_nm IN varchar2 /*, p_out OUT sys_refcursor*/)
is
l_len number;
l_val varchar2(10);
l_val1 varchar2(10);
begin
l_len := length(p_nm);
-- dbms_output.put_line(l_len);
begin
for i in 1..l_len
loop
select REGEXP_SUBSTR (p_nm, '([^,]*)(,|$)',1, i , NULL, 1) into l_val from dual;
-- dbms_output.put_line(l_val);
-- open p_out for
select * into l_val1 from test where nm = l_val;
dbms_output.put_line(l_val1);
exit when l_len is null;
end loop;
exception
when no_data_found then
null;
end;
exception
when others then
dbms_output.put_line('Error reason :'||sqlerrm||' error code :'||sqlcode);
end;
EXECUTE p_test('a,,b,c,d,q,w');
OUTPUT --
a
b
c
d
This procedure is giving me output as i required but i need to get that result into a variable which should be OUT parameter of this procedure as it will be called by the JAVA of our application.
As i have already tried to use the refcursor (please see the commented part) but it is giving me no output while a call it.
Its a call to this procedure when i use the refcursor (by removing comments ).
declare
l_out sys_refcursor;
l_val varchar2(20);
l_str varchar2(20) := 'a,b,c,d';
begin
p_test (l_str, l_out);
loop
fetch l_out into l_val;
dbms_output.put_line(l_val);
dbms_output.put_line('a');
exit when l_out%notfound;
end loop;
end;
So here i got stuck with that how to get the multiple output or am i missing something here and if there is a better approach to this requirement as i have come with this so i am sharing it here.
I am using ---
Oracle Database 11g Enterprise Edition Release 11.2.0.1.0 - 64bit Production
Thanks

1) Create collection in oracle. create type list_of_varchar is table of varchar2(20);
2) Use bulk collect into.
declare
cursor c_obj is select object_name from user_objects where length(object_name) < 20;
arrays list_of_varchar;
begin
open c_obj;
fetch c_obj bulk collect into arrays;
close c_obj;
dbms_output.put_line('Fetched '||arrays.count);
end;
3*) And the same code execute in java.
public static void main(String[] args) throws SQLException, Exception {
Connection con = ConnectionDefinition.getOracleConnection(); //my oracle connection
String str = "declare " +
" cursor c_obj is select object_name from user_objects where length(object_name) < 20;" +
" arrays list_of_varchar;" +
"begin " +
" open c_obj;" +
" fetch c_obj bulk collect into arrays;" +
" close c_obj; " +
" dbms_output.put_line('Fetched '||arrays.count);"+
" ? := arrays;" +
"end;";
CallableStatement cs = con.prepareCall(str);
cs.registerOutParameter(1, OracleTypes.ARRAY, "LIST_OF_VARCHAR");
cs.execute();
String[] strings = (String[])cs.getArray(1).getArray();
for(String tmp : strings) {
System.err.println(tmp);
}
cs.close();
con.close();
}

Related

Running query dynamically inside snowflake sql stored procedure

I need to run the query which is saved in a field in one table and insert the value of query output into another table.
To simplify the ask I have created dummy tables and try to test the logic.
Table 1
create table tsql(id string, q string);
insert into tsql values (1,'Select current_date');
Table tsql has select query in field name q.
Table 2
create table tinput(d date);
Table 2 will get updated from the value in table 1.
Below are the stored procedure I am trying to write. I know this can be done in javascript stored proc but I need to write this in sql as we are following SQL for all other.
Procedure so far.
create or replace procedure sqlreadwrite(id string)
returns string
language sql
as
$$
Declare
select_statement String;
begin
create or replace temp table tk
as select q from tsql where id = :id;
--select_statement := 'insert into tinp values (execute immediate 'Select Q from tk')';
execute immediate 'insert into tinp values (execute immediate 'Select Q from tk')';
return 'Success';
end;
$$;
Till now it is failing.
you don't need to call execute immediate twice, you should call it only once. One thing i wee is you have not specified the list of columns in you INSERT statement , it is always better to specify the list of column.
-- untested
execute immediate 'insert into tinp values (Select Q from tk)';
I figured out a way, however I am open to suggestion and better approach.
Below is the query.
create or replace procedure sqlread(id string)
returns string
language sql
as
$$
Declare
select_statement String;
res resultset;
value string;
res1 resultset;
v string;
begin
create or replace temp table tk as select q from tsql where id = :id;
select_statement := 'Select Q from tk';
res := (execute immediate :select_statement);
let c1 cursor for res;
for row_variable in c1 do
value := row_variable.Q;
res1 := (execute immediate :value);
let c1 cursor for res1;
for row_variable1 in c1 do
v := row_variable1.current_date::date;
insert into tinp values (:v);
end for;
End For;
return v;
end;
$$;
If the target table (tinput) will only accept dates, I wonder what kind of queries are stored in the source table (tsql)? Anyway, I assume there is a business requirement behind this, so your approach should be OK.
Some improvements:
You may remove the TEMP table as it doesn't provide any benefits.
If your procedure will only run 1 query from tinput (if IDs are unique), then you don't need a loop.
Here is the sample code:
create or replace procedure sqlread(p_id string)
returns string
language sql
as
$$
Declare
select_statement String;
res resultset;
value string;
res1 resultset;
v string;
begin
select_statement := 'select q from tsql where id = ?';
res := (execute immediate :select_statement using (P_ID));
let c1 cursor for res;
open c1;
fetch c1 into value;
res1 := (execute immediate :value);
let c2 cursor for res1;
open c2;
fetch c2 into value;
insert into tinput values (:value);
return :value;
exception
when other then
return 'error';
end;
$$;

Snowflake SQL Procedure Parameters

Apologies but I can't seem to find documentation on this.
I am passing in two strings converting them to date to find the number of rows.
Its a silly question but how do I pass in parameters to a SQL query for cursor
CREATE OR REPLACE PROCEDURE LOAD_DATA("START_DATE" VARCHAR(12), "END_DATE" VARCHAR(12))
RETURNS FLOAT
LANGUAGE SQL
EXECUTE AS OWNER
AS '
declare
row_count float;
c1 cursor for select etl_year,etl_month,etl_day_of_year,1 as rowx
from dim_etldate
where etl_date >= to_date(start_date,''dd-mm-yyyy'')
and etl_date <= to_date(end_date,''dd-mm-yyyy'');
begin
row_count := 0.0;
open c1;
for rec in c1 do
row_count := row_count + 1;
end for;
close c1;
return row_count;
end;
';
Error is invalid identifier 'START_DATE'
If you use an argument in the SQL statement you need to put a colon : in front of it, like in this example (observe the id argument):
CREATE OR REPLACE PROCEDURE find_invoice_by_id(id VARCHAR)
RETURNS TABLE (id INTEGER, price NUMBER(12,2))
LANGUAGE SQL
AS
DECLARE
res RESULTSET DEFAULT (SELECT * FROM invoices WHERE id = :id);
BEGIN
RETURN TABLE(res);
END;
More information here.

issue when executing the pgsql complaining about loop

create or replace function f() RETURNS void AS
declare
tableArray text[] := '{"ADDRESS","CONSISTENCY_CHECK","DEPARTMENT_SUPERVISION"}';
tableName CHARACTER VARYING;
value INTEGER ;
BEGIN
FOREACH tableName IN ARRAY tableArray
LOOP
select user_id from tableName where user_id=2631;
if found then
update tableName set user_id=2651 where user_id=2631;
delete from tableName where user_id=2631;
END loop;
end;
here is the error that I get when trying to execute the pgplsql: ERROR syntax error at or near "loop"
There are more issues:
the body of function should be string - you can use apostrophes or more usual and practical $$ custom string separators.
the result of SELECT should not be lost. The clause INTO is missing.
table name should not be a variable - variable cannot be used as table name. In this case you need dynamic SQL - EXECUTE statement.
camel notation for variable names should not be used for SQL language, that is case insensitive
CREATE OR REPLACE FUNCTION f()
RETURNS void AS $$
DECLARE
table_array text[] := '{"ADDRESS","CONSISTENCY_CHECK","DEPARTMENT_SUPERVISION"}';
table_name text;
value integer ;
rc integer
BEGIN
FOREACH table_name IN ARRAY table_array
LOOP
EXECUTE format('SELECT * FROM %I WHERE user_id = $1', table_name) USING 2631;
-- variable FOUND cannot be used for dynamic SQL
GET DIAGNOSTICS rc = ROW_COUNT;
IF rc > 0 THEN
EXECUTE format('UPDATE %I SET user_id = $1 WHERE user_id = $2', table_name) USING 2651, 2631;
EXECUTE format('DELETE %I WHERE user_id = $1', table_name) USING 2631;
END IF;
END LOOP;
END;
$$ LANGUAGE plpgsql;

Converting Oracle Stored Procedure to SQL Server Procedure

I am have created a stored procedure in oracle. Now I need to convert the same procedure to SQL Server procedure. Since I am not good at SQL procedure, please help me.
Following is the Oracle Procedure:
create or replace
PROCEDURE "FTSREMOVESPECIALCHAR"
AS strquery VARCHAR2(4000 byte);
stmt VARCHAR2(1000);
l_cursor SYS_REFCURSOR;
TYPE result_rec IS RECORD (iid NUMBER(10, 0),
fulltextdetails VARCHAR2(4000 byte),
regex VARCHAR2(4000 byte));
l_result_rec RESULT_REC;
idValue NUMBER(10, 0);
fulltextdetailsValue VARCHAR2(4000 byte);
fulltextWithoutSplChr VARCHAR2(4000 byte);
regexValue VARCHAR2(4000 byte);
minmatchValue NUMBER(10, 0);
strQueryinsert VARCHAR2(4000 byte);
BEGIN
dbms_output.ENABLE(1000000);
FOR c IN (SELECT table_name
FROM user_tables
WHERE table_name LIKE 'FULLTEXTLOOKTABLE_%')
LOOP
dbms_output.Put_line(c.table_name);
strquery := 'select ID, FullTextDetails, Regex from ' || c.table_name;
BEGIN
OPEN l_cursor FOR strquery;
LOOP
FETCH l_cursor INTO l_result_rec;
Exit when l_cursor%NOTFOUND;
fulltextdetailsValue := l_result_rec.fulltextdetails;
regexValue := l_result_rec.regex;
dbms_output.Put_line('Before :' ||fulltextdetailsValue);
fulltextdetailsValue := regexp_replace(fulltextdetailsValue, '[^[:alnum:] ]', NULL);
dbms_output.Put_line('After : '||fulltextdetailsValue);
dbms_output.Put_line('Before regexValue:' ||regexValue);
regexValue := replace(regexValue, '([\~\-])', '([\~\-])?');
regexValue := replace(regexValue, '(\!)', '(\!)?');
regexValue := replace(regexValue, '([\#])', '([\#])?');
regexValue := replace(regexValue, '(\#)', '(\#)?');
regexValue := replace(regexValue, '([\$s\&])', '([\$s\&])?');
regexValue := replace(regexValue, '(\%)', '(\%)?');
regexValue := replace(regexValue, '(\^)', '(\^)?');
regexValue := replace(regexValue, Q'[']',Q'['']');
strQueryinsert := 'update '||c.table_name||' set fulltextdetails='''||fulltextdetailsValue||''' where id='||l_result_rec.iid;
dbms_output.Put_line('strQueryinsert : ' ||strQueryinsert);
EXECUTE IMMEDIATE
strQueryinsert;
strQueryinsert := 'update '||c.table_name||' set regex='''||regexValue||''' where id='||l_result_rec.iid;
EXECUTE IMMEDIATE
strQueryinsert;
END LOOP;
EXECUTE IMMEDIATE
'commit';
close l_cursor;
END;
END LOOP;
END;
This procedure will get the all tables from the DB that starts with "FULLTEXTLOOKTABLE_". The table has 4 columns(ID(int), FullTextDetails(nvarchar), Regex(nvarchar), MinMatchCount(int)). For each table it will take the value of "FullTextDetails" column and remove all the special characters and also take the
"Regex" and replace
([\~\-]) with ([\~\-])?
(\!) with (\!)?
([\#]) with ([\#])?
(\#) with (\#)?
([\$s\&]) with ([\$s\&])?
(\%) with (\%)?
(\^) with (\^)?
And update the columns "FullTextDetails" and "Regex" with the new values. Finally the changes are committed.
The most difficult thing here is how to handle the regex replacement as in T-SQL there is not such build in function. Fortunately, you can implement such as I am showing here using SQL CLR integration - basically, you are allowed to write .net objects, to mapped them to SQL objects and use them. You need to do some effort but that's your only chance.
Then, the other things are pretty easy. From this system object you can get and loop through the table names:
SELECT *
FROM [sys].[tables];
You can use cursor but I prefer to skip them. It will be something like this:
DECLARE #Tables TABLE
(
[name] SYSNAME
);
DECLARE #CurrentTableName SYSNAME;
INSERT INTO #Tables ([name])
SELECT [name]
FROM [sys].[tables]
--WHERE [name] LIKE 'FULLTEXTLOOKTABLE_%';
WHILE EXISTS(SELECT 1 FROM #Tables)
BEGIN;
SELECT TOP 1 #CurrentTableName = [name]
FROM #Tables;
SELECT #CurrentTableName;
DELETE FROM #Tables
WHERE [name] = #CurrentTableName;
END;
For the building and executing of the dynamic T-SQL statement you have two options. You can build the whole statement and execute it for each loop like this:
DECLARE #DynamicTSQLStatement NVARCHAR(MAX);
SET #DynamicTSQLStatement = N'UPDATE [dbo].[table] SET [text] = ''test'' WHERE [ID] = 1';
EXEC sp_executesql #DynamicTSQLStatement;
or you can use the sp_executesql with parameters and create a template which is populated with the current values.
I hope this points will be enough to handle the task.

Procedure in PL/SQL with create and cursor

Can we have a Procedure with
First create a table suppose
create table INCOME_GROUP(income_compare_groups varchar(100)) ;
Then insert data into this table.
insert into INCOME_GROUP values (10-20);
Then Use this table into a cursor.
CURSOR c1 IS(select *from INCOME_GROUP);
For Example I am doing this.
BEGIN
create table INCOME_GROUP(income_compare_groups varchar(100)) ;
DECLARE
CURSOR c1 IS(select * income_Group);
BEGIN
FOR acct IN c1 LOOP -- process each row one at a time
INSERT INTO temp_test
VALUES (acct.income_compare_groups);
END LOOP;
COMMIT;
END;
END;
But I am getting some Error.
ORA-06550: line 2, column 4:
PLS-00103: Encountered the symbol "CREATE" when expecting one of the following:
( begin case declare exit for goto if loop mod null pragma
raise return select update while with <an identifier>
<a double-quoted delimited-identifier> <a bind variable> <<
continue close current delete fetch lock insert open rollback
savepoint set sql execute commit forall merge pipe purge
After reading the comments I tried this -
BEGIN
EXECUTE IMMEDIATE 'create table INCOME_GROUP
(
income_compare_groups varchar(100)
)';
DECLARE
CURSOR c1 IS
(select * from
INCOME_GROUP
);
BEGIN
FOR acct IN c1 LOOP -- process each row one at a time
INSERT INTO temp_test
VALUES (acct.income_compare_groups, null);
END LOOP;
COMMIT;
END;
END;
But seems it is not creating table.!!!!
You can do it like this:
create or replace procedure cpy_inc_comp_grps
as
cur_1 sys_refcursor;
compare_group varchar2(100);
begin
execute immediate 'create table income_group(income_compare_groups varchar2(100))';
open cur_1 for 'select income_compare_groups from income_group';
LOOP
FETCH cur_1 INTO compare_group;
DBMS_OUTPUT.PUT_LINE('INSERT INTO temp_test VALUES (rec.income_compare_groups');
EXIT WHEN cur_1%NOTFOUND;
END LOOP;
close cur_1;
execute immediate 'drop table income_group';
end;
And test it with the following code:
begin
cpy_inc_comp_grps;
end;
You have to replace the dbms_output.put_line(...) part with whatever inserts you want to do.
It must be like this:
DECLARE
cur SYS_REFCURSOR;
v_income_compare_groups VARCHAR(100);
BEGIN
EXECUTE IMMEDIATE 'CREATE TABLE INCOME_GROUP(income_compare_groups VARCHAR(100))';
OPEN cur FOR 'SELECT * income_Group';
LOOP
FETCH cur INTO v_income_compare_groups;
EXIT WHEN cur%NOTFOUND;
INSERT INTO temp_test VALUES (v_income_compare_groups);
END LOOP;
CLOSE cur;
COMMIT;
END;
You have to use dynamic Cursor because when you compile the package then the table INCOME_GROUP does not exist yet and you would get an error at CURSOR c1 IS(select * income_Group);
However, there are several issue:
You will get an error if the table already exist. You have to check this first or write an exception handler.
The procedure is useless because you first create an (empty) table and then you select it - it will never select anything!
Try this.
execute immediate 'create table INCOME_GROUP(income_compare_groups varchar(100))';

Resources