Need suggestion with Procedure code error in DB12 Z/OS - stored-procedures

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;

Related

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

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;

DB2 LUW - Get Error Line in Stored Procedure

I'm trying to determine the line in a stored procedure or the last SQL-statement which is causing an error. As a workaround I'm using temporary variables which I manually set to determine in which part of my stored procedure an error occurs.
See the following:
-- Create an ErrorLog table
Create Table SCHEMA.ErrorLog_lrc_test
(
ErrSQLCODE Integer ,
Codepart Char(1),
Type Char(1) ,
MsgText VarChar(1024));
CREATE OR REPLACE PROCEDURE SCHEMA.test_error(IN divisor INT)
LANGUAGE SQL
BEGIN
-- Define variables
DECLARE codepart_var Char(1);
DECLARE test_INT INT;
-- Define sqlcode
DECLARE SQLCODE INTEGER;
--Define Error-Handler
DECLARE CONTINUE HANDLER FOR SQLEXCEPTION
BEGIN
INSERT INTO SCHEMA.ErrorLog_lrc_test(ErrSQLCODE, Codepart, TYPE, MsgText)
VALUES(SQLCODE, codepart_var, 'E', SYSPROC.SQLERRM(SQLCODE));
END;
--Define Warning-Handler
DECLARE CONTINUE HANDLER FOR SQLWARNING, NOT FOUND
BEGIN
INSERT INTO SCHEMA.ErrorLog_lrc_test(ErrSQLCODE, Codepart, TYPE, MsgText)
VALUES(SQLCODE, codepart_var, 'W', SYSPROC.SQLERRM(SQLCODE));
END;
-- Set temporary variable to 'a' to get part of code where error occured
SET codepart_var = 'a';
-- Create Error
sELECT 1/divisor into test_INT
FROM SYSIBM.SYSDUMMY1;
SET codepart_var = 'b';
-- Create Error
sELECT 1/divisor into test_INT
FROM SYSIBM.SYSDUMMY1;
SET codepart_var = 'c';
-- Create Not Found (Sqlcode 100)
INSERT INTO SCHEMA.ErrorLog_lrc_test
SELECT NULL, NULL, NULL, NULL FROM "SYSIBM".SYSDUMMY1
WHERE 1 = 0 ;
END
call SCHEMA.test_error(0);
SELECT *
FROM SCHEMA.ErrorLog_lrc_test;
I get the following:
ERRSQLCODE
CODEPART
TYPE
MSGTEXT
-801
a
E
SQL0801N Division by zero was attempted.
-801
b
E
SQL0801N Division by zero was attempted.
100
c
W
SQL0100W No row was found for FETCH, UPDATE or DELETE; or the result of a query is an empty table.
So I am able to get the part of the code where an error or warning occurs, but it would be better to get the line or the SQL statement as I don't want to specify every part of the code with a temporary variable.
I already found this SQLCA --> sqlerrd(3): "...If an error is encountered during the compilation of an SQL routine, trigger, or dynamic compound SQL (inlined or compiled) statement, sqlerrd(3) contains the line number where the error was encountered". For now I didn't manage to make use of SQLCA variables. I don't know how to implement them in DB2 LUW in a stored procedure.
Is there another/better way to log the specific line or SQL-statement in a stored procedure which is causing an error?
My DB2 version is 10.5.0.
Thank you!
If your Db2-server platform is Linux/Unix/Windows, and you are using a recent version, then consider using DBMS_UTILITY.FORMAT_ERROR_BACKTRACE which may help you.
Documentation here. The documentation includes a worked example.
When using this for stored procedures or routines, it is wise to always create those routines with a meaningful specific name with the SPECIFIC clause on the create or replace statement. Otherwise the routine will have a system generated name which will not be meaningful to users when it appears in the output of DBMS_UTILITY.FORMAT_ERROR_BACKTRACE. There are other reasons you should always use a specific name for your routines.
The SQLCA is for calling programs (i.e. the program that calls the stored procedure).

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''')

Resources