How can I pass a SQL to Cursor which is coming as string in DB2? - db2

I want to pass a SQL to my stored proc as input as a string and my stored proc will pass this SQL as a string to a cursor and provide the output. But a Cursor doesn't accept a SQL as string and we need to prepare the SQL statement and then pass it onto the Cursor. I am not able to make that part working. Please find my sample code here :
Create Procedure abc(IN stmt VARCHAR(500))
SPECIFIC abc
LANGUAGE SQL
DYNAMIC RESULT SETS 1
Re: BEGIN
DECLARE Instmt VARCHAR(500)
PREPARE Instmt FROM stmt;
EXECUTE Instmt;
DECLARE v_cur CURSOR WITH RETURN FOR Instmt;
OPEN v_cur;
END Re
--call abc('select ename,sal from emp')
It gives me error as such that Cursor is expecting a SQL statement. So more or less I am not able to PREPARE the SQL properly to feed it to the CURSOR.
Error Message:
SQL0104N "An expected token "" was found following "".Expected token may include: ""
Please let me know how I could get it working.

Try this one
--#SET TERMINATOR #
CREATE OR REPLACE PROCEDURE TEST_PROC(IN stmt VARCHAR(500))
SPECIFIC TEST_PROC
LANGUAGE SQL
DYNAMIC RESULT SETS 1
BEGIN
DECLARE test_cur CURSOR WITH RETURN TO CALLER FOR test_stmt;
PREPARE test_stmt FROM stmt;
OPEN test_cur;
END#

Related

Temp table for user defined functions

I am trying to use temp table in user defined function for DB2. I am trying to do this in data studio but the code below is not working. How can I make this work?
Thanks.
CREATE FUNCTION BELSIZE.TEST (aSTRING VARCHAR(50))
RETURNS TABLE(
column1 INTEGER
)
F1: BEGIN ATOMIC
DECLARE c1 CURSOR WITH RETURN TO CLIENT FOR stmt;
SET v_dynStmt = 'SELECT 1 column1 from sysibm.sysdummy1';
PREPARE stmt FROM v_dynStmt;
OPEN c1;
RETURN
END
You have syntax errors in your code, more details below.
Apart from syntax errors, your title mentions temp table but your code does not, so your question is poor.
Never write "...is not working", instead write the exact SQLCODE and SQLSTATE and message that you see.
When asking for help with Db2, always write in the question the Db2-version and the operating-system (Z/OS, i-Series, Linux/Unix/Windows) on which the Db2-server runs, because the answer can depend on those facts. Different versions of Db2 for different operating systems have different capabilities and different syntax.
If you want to use cursors for result-sets then use SQL PL stored-procedures, because there are fewer restrictions.
SQL table functions are suitable when you don't need to declare a cursor for result-set.
Db2-LUW prevents you from declaring a cursor in an SQL table function when you use BEGIN ATOMIC.
If you are not using BEGIN ATOMIC, Db2-LUW (current versions, i.e. v11.1) lets you declare a cursor in an SQL UDF but you cannot use that cursor directly to return the result-set, as you can do inside SQL PL stored procedures.
For your example, the syntax below is valid and also useless, so consider using an SQL PL procedure instead:
--#SET TERMINATOR #
CREATE or replace FUNCTION BELSIZE.TEST (aSTRING VARCHAR(50))
RETURNS TABLE( column1 INTEGER)
language sql
specific belsize.test
BEGIN atomic
RETURN select 1 as column1 from sysibm.sysdummy1 ;
END
#
select * from table(belsize.test('a')) as t
#

How to create a stored procedure including the "SELECT" in Oracle SQL Developer?

I am novice user in oracle and well I am creating a stored procedure to display data from a table, because my teaching process requires it. At first I ran my query follows.
Create or replace procedure p_ mostrar
Is
Begin
Select ID_MODULO, NOMBRE, URL, ESTADO, ICONO FROM MODULO WHERE ESTADO=1 ;
Commit;
End p_mostrar;
And he throws me the following error:
The judgment was expected INTO" After some research changed the syntax and run it as follows:
Create or replace procedure p_ mostrar (C1 out sys_refcursor)
Is
Begin Open C1 for Select ID_MODULO, NOMBRE, URL, ESTADO, ICONO
FROM MODULO
WHERE ESTADO=1 ;
Commit;
End p_mostrar;
And I think runs correctly. But now it does not know how to run the procedure. I thank you in advance and expect a prompt response. Remember, I'm learning with Oracle SQL Developer.
When you are dealing with select statement inside a stored procedure, you need to include INTO clause to the select statement to store the values in a variable. Try this
CREATE OR REPLACE PROCEDURE p_mostrar
IS
v_id_modulo modulo.id_modulo%TYPE;
v_nombre modulo.nombre%TYPE;
v_url modulo.url%TYPE;
v_estado modulo.estado%TYPE;
v_icono modulo.icono%TYPE;
BEGIN
SELECT id_modulo, nombre, url, estado, icono
INTO v_id_modulo, v_nombre, v_url, v_estado, v_icono --needed to catch the values selected and store it to declared variables
FROM modulo
WHERE estado=1 ;
Commit; -- i dont think this is necessary, you use commit statement only when you use DML statements to manage the changes made
dbms_output.put_line(v_id_modulo||' '|| v_nombre||' '||v_url||' '||v_estado||' '||v_icono); --used to display the values stored in the variables
END;
This should work:
var result refcursor
execute p_ mostrar(:result)

Define variable and run a query using EXECUTE BLOCK

I have a large query in Firebird (which I run using FlameRobin), using a parameter all over the place, but getting the query below to run will do:
SELECT * FROM customers WHERE customerid = 1234;
I want to define 1234 as a variable, say customerID, so that I can easily replace it with something else.
I've learned that I need to put this inside a EXECUTE BLOCK.
EXECUTE BLOCK
AS
DECLARE customerID INT = 1234;
BEGIN
SELECT * FROM customers WHERE customerid = :customerID
END
If of any importance, the error I am getting is Engine Message :
Dynamic SQL Error
SQL error code = -104
Unexpected end of command - line 3, column 26
The problem is that FlameRobin needs to know when a statement ends and the next statement starts. By default it uses the semicolon (;) for this. However an EXECUTE BLOCK is essentially a stored procedure that isn't stored in the database, so it contains PSQL code that also uses the semicolon as a statement separator.
The consequence of this is that you get syntax errors because FlameRobin is sending incomplete statements to the server (that is: it is sending a statement after each ; it encounters).
You need to instruct FlameRobin to use a different statement terminator using SET TERM. Other Firebird query tools (eg isql) require this as well, but it is not actually part of the syntax of Firebird itself!
So you need to execute your code as:
-- Instruct flamerobin to use # as the terminator
SET TERM #;
EXECUTE BLOCK
AS
DECLARE customerID INT = 1234;
BEGIN
SELECT * FROM customers WHERE customerid = :customerID;
END#
-- Restore terminator to ;
SET TERM ;#
However doing this will still result in an error, because this query is invalid for PSQL: A SELECT in a PSQL block requires an INTO clause to map the columns to variables. And to get the values out of the EXECUTE BLOCK returned to FlameRobin you also need to specify a RETURNS clause as described in the documentation of EXECUTE BLOCK:
-- Instruct flamerobin to use # as the terminator
SET TERM #;
EXECUTE BLOCK
RETURNS (col1 INTEGER, col2 VARCHAR(100))
AS
DECLARE customerID INT = 1234;
BEGIN
SELECT col1, col2 FROM customers WHERE customerid = :customerID INTO :col1, :col2;
SUSPEND;
END#
-- Restore terminator to ;
SET TERM ;#
As far as I know the SUSPEND is technically not required here, but Flamerobin will not fetch the returned row if it isn't included.
However the above will not work if the select produces multiple rows. For that you need to use FOR SELECT ... DO combined with a SUSPEND:
-- Instruct flamerobin to use # as the terminator
SET TERM #;
EXECUTE BLOCK
RETURNS (col1 INTEGER, col2 VARCHAR(100))
AS
DECLARE customerID INT = 1234;
BEGIN
FOR SELECT col1, col2 FROM customers WHERE customerid = :customerID INTO :col1, :col2
DO
SUSPEND;
END#
-- Restore terminator to ;
SET TERM ;#
The SUSPEND here returns the row and waits until the caller has fetched that row and then continues with the FOR loop. This way it will iterate over the results.
IMHO this too much effort for parameterization. You might want to consider simply not parametrizing when you use FlameRobin, or use a tool that supports asking for parameter values for the normal parameter placeholders of Firebird (but to be honest I am not sure if there are any).
Well you need a input and return value, ex:
EXECUTE BLOCK (p_customerID integer=?)
returns(col_1 type of column table.id_cv)
as
begin
for
select col_1 from table
where customerID=:p_customerID
into col_1 do
suspend;
end
Delphi as client side:
myquery1.ParamByName('p_customerID ').Value:=1234;
myquery1.Open()
if not myquery1.IsEmpty then
begin
// using returning values
if myquery1.FieldbyName('col_1').AsString='this' then DoThat();
end;
Use this way ever yout need input parameters and/or returning values;

How do I fix this PostgreSQL 9.1 stored procedure?

We have a problem with our software and in order to correct the issue, I have to write a stored procedure that will be run as part of the upgrade process for upgrade installs. This stored procedure needs to find every row in a particular table that matches certain conditions and update that row. For internal reasons, the update has to be done through a stored procedure we wrote specifically for inserting and updating data.
Here is the stored procedure I have written to fix this issue:
CREATE OR REPLACE FUNCTION FixDataProblem() RETURNS VOID AS $$
DECLARE
FixCursor NO SCROLL CURSOR FOR
SELECT * FROM MyTable WHERE ProblemColumn IN ( '?', 'PR' );
RowToUpdate MyTable%ROWTYPE;
BEGIN
-- Open the cursor
OPEN FixCursor;
-- Start a loop
LOOP
-- Fetch the next row from thr cursor
FETCH FixCursor INTO RowToUpdate;
-- Did we get anything back?
IF RowToUpdate IS NULL THEN
-- We didn't. Exit the loop
EXIT;
END IF;
-- Call the UpsertMyTable stored procedure to set the ProblemColumn column to NULL
SELECT CarSystem.UpsertMyTable( RowToUpdate.RowId,
RowToUpdate.ForeignId,
RowToUpdate.CountryId,
NULL,
RowToUpdate.Plate,
RowToUpdate.HashedData,
RowToUpdate.PlateClassId,
RowToUpdate.AlarmClassId,
RowToUpdate.BeginDate,
RowToUpdate.EndDate,
RowToUpdate.ListPriorityId,
RowToUpdate.VehicleTypeId,
RowToUpdate.MakeId,
RowToUpdate.ModelId,
RowToUpdate.Year,
RowToUpdate.ColorId,
RowToUpdate.Notes,
RowToUpdate.OfficerNotes,
NULL,
UUID_GENERATE_V4() );
END LOOP;
-- Close the cursor
CLOSE ListDetailsCursor;
END;
$$ LANGUAGE plpgsql;
This stored procedure fine, but when I run it, I get:
ERROR: query has no destination for result data
HINT: If you want to discard the results of a SELECT, use PERFORM instead.
CONTEXT: PL/pgSQL function "fixdataproblem" line 22 at SQL statement
********** Error **********
ERROR: query has no destination for result data
SQL state: 42601
Hint: If you want to discard the results of a SELECT, use PERFORM instead.
Context: PL/pgSQL function "fixdataproblem" line 22 at SQL statement
How do I fix this issue? I believe I am calling the stored procedure correctly. I really don't know what the issue with this stored procedure is.
Thanks
Tony
It says right there:
ERROR: query has no destination for result data
HINT: If you want to discard the results of a SELECT, use PERFORM instead.
CONTEXT: PL/pgSQL function "fixdataproblem" line 22 at SQL statement
And on line 22:
-- Call the UpsertMyTable stored procedure to set the ProblemColumn column to NULL
SELECT CarSystem.UpsertMyTable( RowToUpdate.RowId,
...
Change it from SELECT to PERFORM. See PERFORM for why.

How to execute an SQL string in DB2

How do I execute an SQL string statement in DB2? I'm using IBM Data Studio.
Do you mean executing a dynamic SQL string? Something like:
DECLARE stmt VARCHAR(1000);
DECLARE my_table VARCHAR(50);
SET my_table = 'DEPT_'||deptNumber;
SET stmt = 'SELECT * FROM '||my_table;
PREPARE s1 FROM stmt;
EXECUTE s1;
You can only do that in a stored proc though. One defined as CREATE PROCEDURE GetDeptInfo (deptNumber VARCHAR(5)) for this example. Read about EXECUTE and PREPARE in the db2 docs http://publib.boulder.ibm.com/infocenter/db2luw/v9r5/index.jsp
After days of researching I found how to write and run dynamic SQL on DB2:
create or replace procedure Search ()
BEGIN
DECLARE v_dynamicSql varchar(2000);
SET v_dynamicSql = 'INSERT INTO dictonary(name) values(' || 'dynamicSQL in db2' ||')';
EXECUTE IMMEDIATE v_dynamicSql;
END;
Hope to help someone.
What difficulty are you encountering?
There are likely lots of ways to do it. Here's one:
File -> New -> Other -> SQL or XQuery script
You may need to create a project or define a database connection.
Enter the SQL code.
Script -> Run script.
Results will show up at the bottom of your screen.
In Control Center, right click the database, you will see "Query". Click on it and you are good to go.