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''')
Related
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;
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;
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
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;
/
Hi I have a requirement where I get list of values to a input parameter in PL/SQL procedure. The size of the input list varies which is dynamic. How to handle this requirement any help?
CREATE OR REPLACE PACKAGE PKG_TEST AS
TYPE X IS TABLE OF VARCHAR2(30);
PROCEDURE XYZ(Y IN X);
END PKG_TEST;
/
The type can be declared as "TABLE OF" OR "VARRAY(10)";
CREATE OR REPLACE PACKAGE BODY PKG_TEST AS
PROCEDURE XYZ(Y IN X) AS
BEGIN
FOR I IN Y.FIRST..Y.LAST
LOOP
DBMS_OUTPUT.PUT_LINE('THE VALUE OF I IS'||Y(I));
END LOOP;
END;
END PKG_TEST;
/
DECLARE
BEGIN
PKG_TEST.XYZ('1','2','3','4');
END;
/
You could use a varchar parameter in sql, each value must be separated by a comma, something like this:
'value1,value2,value3,value4,...,'
So, you can read the values using the function split of sql
I hope that I understood your question