How to use EXECUTE INTO statement in db2 iSeries as400 db? - stored-procedures

I have a procedure with the following lines:
The value of ITERATOR is set dynamically in a loop
DECLARE V_LOSS_ID integer;
DECLARE V_STATE_CODE CHARACTER(2);
DECLARE V_CLASS_CODE CHARACTER(4);
SET V_QUERY = 'set (?,?,?) = (select LOSS_ID, STATE_CODE, CLASS_CODE from LOSS L limit 1 offset '|| ITERATOR || ' )';
PREPARE STMT FROM V_QUERY;
EXECUTE STMT into V_LOSS_ID, V_STATE_CODE, V_CLASS_CODE;
I am getting the following error in iSeries db2 -
[SQL0104] Token V_LOSS_ID was not valid. Valid tokens: SQL DESCRIPTOR.
However, this procedure works on DB2 LUW database. Facing this issue only on DB2 iseries as400 database

EXECUTE in Db2 for IBM i has different functionality.
So, use something like below:
DECLARE ITERATOR ...;
DECLARE V_LOSS_ID integer;
DECLARE V_STATE_CODE CHARACTER(2);
DECLARE V_CLASS_CODE CHARACTER(4);
DECLARE C1 CURSOR FOR STMT;
...
--SET V_QUERY = 'set (?,?,?) = (select LOSS_ID, STATE_CODE, CLASS_CODE from LOSS L limit 1 offset '|| ITERATOR || ' )';
SET V_QUERY = 'select LOSS_ID, STATE_CODE, CLASS_CODE from LOSS L limit 1 offset '|| ITERATOR;
PREPARE STMT FROM V_QUERY;
--EXECUTE STMT into V_LOSS_ID, V_STATE_CODE, V_CLASS_CODE;
OPEN C1;
FETCH C1 INTO V_LOSS_ID, V_STATE_CODE, V_CLASS_CODE;
CLOSE C1;

Related

Need suggestion with Procedure code error in DB12 Z/OS

can someone help with my Proc code here? I am trying to execute this proc in DB12 Z/OS. Below is the body of my proc
CREATE PROCEDURE DEL_TBL_TEST23(IN TBL_NM VARCHAR(100))
DYNAMIC RESULT SETS 1
LANGUAGE SQL MODIFIES SQL DATA
BEGIN
DECLARE SQLCODE INTEGER;
DECLARE BDE_COUNTER INTEGER DEFAULT 0;
DECLARE V1 VARCHAR(50);
DECLARE V2 VARCHAR(100);
DECLARE V3 VARCHAR(100);
DECLARE V4 VARCHAR(100);
DECLARE V5 VARCHAR(200);
DECLARE V6 VARCHAR(500);
DECLARE T VARCHAR(500);
SET V1='DELETE FROM';
SET V2= TBL_NM;
SET V3='WHERE LN_NO IN (SELECT LN_NO FROM';
SET V4= TBL_NM;
SET V5='WHERE REC_CHNG_CD=''T''';
SET V6='ORDER BY LN_NO FETCH FIRST 10000 ROWS ONLY)';
SET T = V1||V2||V3||V4||V5||V6 ;
DEL_LOOP:
LOOP
SET BDE_COUNTER=BDE_COUNTER + 1;
EXECUTE IMMEDIATE T;
COMMIT;
IF SQLCODE = 100 THEN
LEAVE DEL_LOOP;
END IF;
END LOOP DEL_LOOP;
COMMIT;
END
I want to delete some rows from a tablename, which I will provide while executing it. My proc here keeps on running in loops. Please help me know what am I doing wrong here?
Note that there is only one SQLCODE that always reflects the result of the last SQL-Statement. So instead of checking the SQLCODE from EXECUTE IMMEDIATE T you are seeing that of the COMMIT that will never be 100.
So this might work:
DEL_LOOP:
LOOP
SET BDE_COUNTER=BDE_COUNTER + 1;
EXECUTE IMMEDIATE T;
IF SQLCODE = 100 THEN
LEAVE DEL_LOOP;
END IF;
COMMIT;
END LOOP DEL_LOOP;
COMMIT;

How to call a stored procedure (not function) with INOUT parameter in PostgreSQL 13

I have this stored procedure :
CREATE OR REPLACE PROCEDURE SP_ObtenerSecuencialFactura(INOUT p_secuencial INT)
LANGUAGE PLPGSQL
AS
$$
BEGIN
SELECT MAX("CODIGOFACTURA") + 1 INTO p_secuencial FROM "FACTURA";
IF p_secuencial IS NULL THEN
p_secuencial := 1;
END IF;
END
$$
And the calling:
DECLARE secuencial INT;
CALL SP_ObtenerSecuencialFactura(secuencial);
RAISE NOTICE '%', secuencial;
But I get this error when I call that stored procedure:
ERROR: syntax error at or near "INT"
LINE 1: DECLARE secuencial INT;
What's wrong? I was finding examples but only exist with functions.
This is the solution:
DO
$$
DECLARE secuencial INT;
BEGIN
CALL SP_ObtenerSecuencialFactura(secuencial);
RAISE NOTICE '%', secuencial;
END
$$
NOTICE: 1
DO
Query returned successfully in 85 msec.
PostgreSQL use PL/pgSQL like Oracle with PL/SQL, so, to call a Store Procedure with OUTIN parameter, we need envolved the calling and the variable in Anonymous Block with "do" and "$$"
DO in PostgreSQL

Loop results of procedure execution in pl/sql

I want to make stored procedure which going to check Oracle db tables and do statistics gathering for them.
I have made this so far, and I would like to now how can I catch a result of execution for each line in a same loop?
declare
comm varchar2(200);
cursor c1 is
select owner, table_name, num_rows, last_analyzed from dba_tables
where num_rows>=500000
and owner not in ('SYS','SYSTEM')
and last_analyzed <= sysdate -7
order by 3 desc;
begin
FOR V1 IN C1 LOOP
comm:= 'EXEC DBMS_STATS.gather_table_stats( '''||V1.OWNER||''','''||V1.TABLE_NAME||''' , estimate_percent => DBMS_STATS.auto_sample_size);';
EXECUTE IMMEDIATE comm;
end loop;
end;
From a purely syntactical perspective, your code could be something like this:
begin
for r in (
select t.owner, t.table_name, t.num_rows
from dba_tables t
left join dba_external_tables x
on x.owner = t.owner and x.table_name = t.table_name
where t.owner not in ('SYS','SYSTEM')
and t.temporary = 'N'
and x.table_name is null
and t.num_rows >= 500000
and t.last_analyzed <= sysdate -7
)
loop
begin
dbms_stats.gather_table_stats(r.owner, r.table_name);
exception
when others then
dbms_output.put_line('Error gathering stats on table '|| r.owner||'.'||r.table_name||': '|| sqlerrm);
end;
end loop;
end;
I have excluded external and temporary tables, and failures are skipped.
However, I'm not sure this is a good approach.
Are you sure you want to gather stats for every table in every other schema?
What should happen with tables that don't have any stats?
Are you sure you want to use the default method_opt setting for every table? Some tables might have carefully crafted histograms and extended statistics column groups, or they might deliberately avoid histograms altogether.
Could there be tables with huge volumes that will take many hours to process? If so, are you ready for your script to run for several days?
I tried and this will do for now.... Thank you for your replies
declare
comm varchar2(200);
cursor c1 is
select owner, table_name, num_rows, last_analyzed from dba_tables
where num_rows>=500000
and owner not in ('SYS','SYSTEM')
and last_analyzed <= sysdate -7
order by 3 desc;
begin
FOR V1 IN C1 LOOP
begin
comm := 'begin DBMS_STATS.gather_table_stats( '''||V1.OWNER||''','''||V1.TABLE_NAME||''' , estimate_percent => DBMS_STATS.auto_sample_size); end;';
--dbms_output.put_line('begin DBMS_STATS.gather_table_stats( '''||V1.OWNER||''' , '''||V1.TABLE_NAME||''' , estimate_percent => DBMS_STATS.auto_sample_size); end;') ;
EXECUTE IMMEDIATE comm ;
dbms_output.put_line( V1.TABLE_NAME || '------Ok------') ;
exception
when others then
dbms_output.put_line(V1.TABLE_NAME || '------Not ok------' || 'SQLERRM: '|| SQLERRM || ' Format error stack : '||dbms_utility.format_error_stack) ;
end;
end loop;
end;

Handling multi-valued parameter in DB2 Stored Procedure

I have a Stored Procedure with a input param, which is a multi valued parameter of Varchar.
The passed parameters are passed to the IN Clause of the query.
I am unable to figure out how to handle that in the stored procedure.
Till now , I have this (this is the snippet of the actual stored procedure) :
CREATE OR replace PROCEDURE <SCHEMA>.Some_Proc
(
IN V_INDSTRY_DESCRPTN VARCHAR (2000)
)
DYNAMIC RESULT SETS 1
BEGIN
DECLARE WHERE_CLAUSE VARCHAR(5000) DEFAULT '';
DECLARE OUTER_CLAUSE VARCHAR(2000) DEFAULT '';
DECLARE V_SQL VARCHAR(10000) DEFAULT '';
DECLARE CSR_RSLT_SET CURSOR WITH RETURN FOR S1;
IF (V_INDSTRY_DESCRPTN != 'ALL') THEN
SET WHERE_CLAUSE = WHERE_CLAUSE || 'AND industry.INDSTRY_DESCRPTN in ( '''||V_INDSTRY_DESCRPTN||''')' ;
END IF;
SET V_SQL ='<SOME QUERY>'
/*some other logic goes here*/
PREPARE S1 FROM V_SQL;
OPEN CSR_RSLT_SET;
END
I am calling the procedure like this :
CALL <SCHEMA>.Some_Proc ('industry1')
1)how do I send multiple values in the same parameter?
CALL <SCHEMA>.Some_Proc ("'industry1','industry2'") gives a compilation error
2)how do I handle the multi-valued parameter within the procedure.
Try this as is:
--#SET TERMINATOR #
CREATE OR REPLACE PROCEDURE TEST_MULTIVALUE(P_TABSCHEMAS VARCHAR(128))
DYNAMIC RESULT SETS 1
BEGIN
DECLARE L_STMT VARCHAR(200);
DECLARE C1 CURSOR WITH RETURN FOR S1;
SET L_STMT = 'SELECT TABSCHEMA, TABNAME FROM SYSCAT.TABLES WHERE TABSCHEMA IN ('||P_TABSCHEMAS||')';
CALL DBMS_OUTPUT.PUT_LINE(L_STMT);
PREPARE S1 FROM L_STMT;
OPEN C1;
END#
SET SERVEROUTPUT ON#
CALL TEST_MULTIVALUE('''SYSCAT''')#
CALL TEST_MULTIVALUE('''SYSCAT'', ''SYSSTAT''')#
For those who are afraid of sql injections
We tokenize the input parameter with strings separated by comma producing a table of strings.
CREATE OR REPLACE PROCEDURE TEST_MULTIVALUE_STATIC(P_TABSCHEMAS VARCHAR(128))
DYNAMIC RESULT SETS 1
BEGIN
DECLARE C1 CURSOR WITH RETURN FOR
SELECT TABSCHEMA, TABNAME
FROM SYSCAT.TABLES T
WHERE EXISTS
(
SELECT 1
FROM XMLTABLE
(
'for $id in tokenize($s, "\s*,\s*") return <i>{string($id)}</i>'
passing P_TABSCHEMAS as "s"
COLUMNS
TOK VARCHAR(128) PATH '.'
) P
WHERE P.TOK=T.TABSCHEMA
);
OPEN C1;
END
#
CALL TEST_MULTIVALUE_STATIC('SYSCAT')#
CALL TEST_MULTIVALUE_STATIC('SYSCAT, SYSSTAT')#
I guess your question actually is, how to escape single quotes inside a character literal (surrounded by single quotes) in the standard-compliant SQL. The answer is, by doubling them:
CALL <SCHEMA>.Some_Proc ('''industry1'',''industry2''')

DEFINE vs DECLARE - escaping quotes

I have defined a variable
define myStrings = "'abc','def'"
which I later need to use inside a procedure block and convert into a table of varchars
declare
type varcharListType is table of varchar(200);
myList varcharListType;
begin
myList := varcharListType(&myStrings);
.
.
.
end;
/
I am attempting to use either the variable or the table inside an IN clause in a create query within the procedure block
execute immediate 'create table tmp_foo '
|| 'as select * from bar '
|| 'where bar_val in (&myStrings) ';
I have tried using the REPLACE function also
myNewStrings := replace(&myStrings, '''' , '''''');
but I get an exception related to abc and def not being defined.
ISSUE:
I am getting a syntax exception because the quotes around abc and def in myString are not escaped. The value "'abc','def'" must be 'defined' rather then 'declared' so it is substituted later.
QUESTION:
Is it possible to 'define' a variable in such a way that I can use it both as table type values and also a string in the execute immediate statement?
TO REPRODUCE:
Create
create table bar (bar_id number not null, bar_val varchar2(20),
constraint bar_pk primary key (bar_id)
enable
);
Insert
insert into bar (bar_id, bar_val)
values (1, 'abc'),
(2, 'def'),
(3, 'ghi');
SAMPLE PROCEDURE
set verify off;
set serveroutput on;
define myStrings = "'abc','def'"
declare
type varcharListType is table of varchar(20);
myList varcharListType;
begin
myList := varcharListType(&myStrings);
execute immediate 'create table tmp_foo '
|| 'as select * from bar '
|| 'where bar_val in (&myStrings) ';
for i in myList.FIRST..myList.LAST loop
dbms_output.put_line('VALUE: ' || myList(i));
end loop;
end;
/
set serveroutput off;
set verify on;
The below is the approch I would take, Note the use of tablen in the loop, this is because the DBMS_UTILITY.COMMA_TO_TABLE procedure adds a null value at the end of the table.
Hope you find this helpfull
declare
myStrings varchar2(100) := '''abc'',''def''';
myList dbms_utility.uncl_array;
tablen number :=0;
begin
DBMS_UTILITY.COMMA_TO_TABLE ( replace(myStrings, '''', ''), tablen, myList);
execute immediate 'create table tmp_foo '
|| 'as select * from bar '
|| 'where bar_val in (' ||myStrings||')';
for i in myList.FIRST..tablen loop
dbms_output.put_line('VALUE: ' || myList(i));
end loop;
end;
/
Thanks to #ShaunPeterson for inspiring the solution to this issue. While it solve the issue directly it provided the correct approach so all +1s should go to him.
Where his answer fell short was that he 'declared' myStrings rather then 'defining' it.
declare
myStrings varchar2(100) := '''abc'',''def''';
NOT
define myStrings = "'abc','def'"
Herein lay the crux of the issue. In PL/SQL variables that are 'declared' for a procedure block like myStringsVar below are not substituted like 'defined' variables are. As per the OP the requirement was that 'myStrings' was first 'defined' then later transformed for use in a procedure block.
Therefore the resulting solution looks like this:
define myStrings = "''abc'',''def''"
declare
myStringsVar varchar2(100) := '&myStrings';
myList dbms_utility.uncl_array;
tablen number :=0;
begin
DBMS_UTILITY.COMMA_TO_TABLE ( replace(myStringsVar, '''', ''), tablen, myList);
execute immediate 'create table tmp_foo '
|| 'as select * from bar '
|| 'where bar_val in (' || myStringsVar||')';
for i in myList.FIRST..tablen loop
dbms_output.put_line('VALUE: ' || myList(i));
end loop;
end;
/

Resources