Trying to use temporary table in IBM DB2 and facing issues - db2

I am getting the following error while creating a stored procedure for testing purpose:
SQL Error [42601]: An unexpected token "DECLARE GLOBAL TEMPORARY TABLE
SESSION" was found following "RSOR WITH RETURN FOR". Expected tokens may include: "".. SQLCODE=-104, SQLSTATE=42601, DRIVER=4.21.29
Code:
CREATE OR REPLACE PROCEDURE Test ( IN GE_OutPutType SMALLINT)
----------------------------------------------------------------------------------------------------
DYNAMIC RESULT SETS 1 LANGUAGE SQL
BEGIN
DECLARE C CURSOR WITH RETURN FOR DECLARE GLOBAL TEMPORARY TABLE
SESSION.TEMP (DATE CHAR(10) NOT NULL,
SALARY DECIMAL(9,
2) ,
COMM DECIMAL(9,
2));
INSERT
INTO
SESSION.TEMP (DATE,
SALARY,
COMM) SELECT
VARCHAR_FORMAT(CURRENT_DATE,
'MM/DD/YYYY'),
10.2,
11.5
FROM
sysibm.sysdummy1
IF GE_OutPutType = 1
BEGIN
SELECT
*
FROM
TEMP
ELSEIF GE_OutPutType = 2 SELECT
'HEADER' CONCAT SPACE(1979) CONCAT 'H'
FROM
sysibm.sysdummy1
END OPEN C;
END

Your syntax is not valid.
You must declare your temporary table independently of your cursor.
You cannot combine these in a single statement.
Use dynamic-SQL features to achieve what you need.
Use instead the format:
Declare c1 cursor with return to caller for Statement1
and
set v_cursor_text = 'select ... from session.temp ; `
then use
prepare Statement1 from v_cursor_text;
and before you exit the stored procedure you need to leave the cursor opened:
open c1;
Do study the Db2 online documentation to learn more about these features.
Here is a small fragment of your procedure showing what I mean:
CREATE OR REPLACE PROCEDURE mytest ( IN GE_OutPutType SMALLINT)
DYNAMIC RESULT SETS 1
LANGUAGE SQL
specific mytest
BEGIN
DECLARE v_cursor_text varchar(1024);
DECLARE C1 CURSOR WITH RETURN FOR Statement1;
DECLARE GLOBAL TEMPORARY TABLE SESSION.TEMP (
DATE CHAR(10) NOT NULL,
SALARY DECIMAL(9,
2) ,
COMM DECIMAL(9,
2))
with replace on commit preserve rows not logged;
INSERT INTO SESSION.TEMP (DATE, SALARY, COMM)
SELECT VARCHAR_FORMAT(CURRENT_DATE, 'MM/DD/YYYY'),
10.2,
11.5
FROM sysibm.sysdummy1 ;
if GE_OutPutType = 1
then
set v_cursor_text = 'select * from session.temp';
end if;
if GE_OutPutType = 2
then
set v_cursor_text = 'select ''header'' concat space(1979) concat ''H'' from sysibm.sysdummy1';
end if;
prepare Statement1 from v_cursor_text;
open c1;
END#

Related

Cannot Get Dynamic Exec of SP to Return INOUT Param

Using PostgreSQL 13.2, wherein a stored procedure (the Requestor) is given a name of a list of stored procedures to run (the job group). All sp's executed this way are coded to write a log record as their last task. I have chosen to pull that 'append log' code from all of the sp's, and instead send back the log record (always a single record) using an INOUT rowtype param, but have run into trouble. In my example below, the requestor sp will load the records returned from the sp's it calls into a temp table shaped like the permanent log table.
That permanent table looks like this:
create table public.job_log (
log_id integer,
event_id integer,
job_id integer,
rows_affected integer);
Any one of the jobs that is executed by the requestor sp might look like this one:
CREATE OR REPLACE procedure public.get_log_rcd(
inout p_log_rcd public.job_log)
LANGUAGE 'plpgsql'
as
$BODY$
declare
v_log_id integer = 40;
v_event_id integer = 698;
v_job_id integer = 45;
v_rows_affected integer = 60;
begin
select
v_log_id
, v_event_id
, v_job_id
, v_rows_affected
into
p_log_rcd.log_id,
p_log_rcd.event_id,
p_log_rcd.job_id,
p_log_rcd.rows_affected;
end;
$BODY$
This sample sp doesn't do anything--it's purpose here is only to simulate initialize of the log parameters to return to caller.
Again, the requestor sp that's going to run jobs like the one above creates a temp table with the same structure as the permanent log:
drop table if exists tmp_log_cache;
create temp table tmp_log_cache as table public.job_log with no data;
If the requestor sp didn't have to do dynamic SQL, it would look something like this block here:
do
$$
declare
big_local public.job_log;
begin
call public.get_log_rcd( big_local );
insert into tmp_log_cache (
log_id
, event_id
, job_id
, rows_affected )
values (
big_local.log_id
, big_local.event_id
, big_local.job_id
, big_local.rows_affected);
end;
$$;
Doing a
select * from tmp_log_cache;
Returns a row containing the 4 column values expected, all is well. But, dynamic execution is required. And, as I'm sure most folks here know, the following dog don't hunt:
do
$$
declare
big_local public.job_log;
v_query_text varchar;
v_job_name varchar = 'public.get_log_rcd';
begin
select 'call ' || v_job_name || '( $1 );'
into v_query_text;
execute v_query_text using big_local::public.job_log;
insert into tmp_log_cache (
log_id
, event_id
, job_id
, rows_affected )
values (
big_local.log_id
, big_local.event_id
, big_local.job_id
, big_local.rows_affected);
end;
$$;
The above dynamic statement executes without error, but the insert statement only has NULL values to work with--a row is inserted, all nulls. Any suggestions warmly welcomed. The sp's that comprise the various job groups could probably have been implemented as functions, although in all cases their primary tasks are to massage, normalize, cleanse telemetry data, not to spit anything out, per se.
Hmm, the documentation states that "parameter symbols (...) only work in SELECT, INSERT, UPDATE, and DELETE commands.", so this probably isn't possible using parameters.
But as a workaround you can build a dynamic DO and include a variable to get the values and the INSERT in there.
DO
$o$
DECLARE
v_query_text varchar;
v_job_name varchar := format('%I.%I',
'public',
'get_log_rcd');
BEGIN
v_query_text := concat('DO ',
'$i$ ',
'DECLARE ',
' big_local public.job_log; ',
'BEGIN ',
' CALL ', v_job_name, '(big_local); ',
' INSERT INTO tmp_log_cache ',
' (log_id, ',
' event_id, ',
' job_id, ',
' rows_affected) ',
' VALUES (big_local.log_id, ',
' big_local.event_id, ',
' big_local.job_id, '
' big_local.rows_affected); ',
'END; ',
'$i$; ');
EXECUTE v_query_text;
END;
$o$;
db<>fiddle
Thanks--I would not have considered the ability to execute a 'do' using execute. It just would not have occurred to me. Well, here's my solution: flip to functions.
With the understanding that my 'Requestor' is only given sp's to run because that's what we had to do with SQL Server and it was reflex, I did the 1-line change needed to flip my example sp above to a function:
CREATE OR REPLACE function public.get_log_rcdf(
inout p_log_rcd public.job_log)
returns public.job_log
LANGUAGE 'plpgsql'
as
$BODY$
declare
v_log_id integer = 40;
v_event_id integer = 698;
v_job_id integer = 45;
v_rows_affected integer = 60;
begin
select
v_log_id
, v_event_id
, v_job_id
, v_rows_affected
into
p_log_rcd.log_id,
p_log_rcd.event_id,
p_log_rcd.job_id,
p_log_rcd.rows_affected;
end;
$BODY$
In fact, the change to a function required the addition of a RETURNS line. Done. Then, the dynamic call was tweaked to a SELECT and the execute modified with an INTO:
do
$$
declare
big_local public.job_log;
v_query_text varchar;
v_job_name varchar = 'public.get_log_rcdf';
begin
select 'select * from ' || v_job_name || '( $1 );'
into v_query_text;
raise info 'SQL text is: %', v_query_text;
execute v_query_text into big_local using big_local;
insert into tmp_log_cache (
log_id
, event_id
, job_id
, rows_affected )
values (
big_local.log_id
, big_local.event_id
, big_local.job_id
, big_local.rows_affected);
end;
$$;
and the process now works exactly as desired. I tidy up my handling of the dynamic function name as illustrated in the first answer, and I think we're done here.

Create the query as a string and execute that in PostgreSQL 10

I am trying to create the query as a string and execute that in PostgreSQL 10.
As far as I know, I can use the EXECUTE command to execute my query from a defined string.
Unfortunately, I have got an error: SQL Error [42601]: ERROR: syntax error at or near "execute"
Below is my code:
drop table if exists delinquent;
create table delinquent
(
report_date date
,account_id text
)
;
INSERT INTO delinquent VALUES('2019-07-23', 'a1234');
INSERT INTO delinquent VALUES('2019-07-23', 'b5679');
--------------
drop table if exists output1;
create temp table output1
(
report_date date
,account_id text
)
;
--------------
do $$
declare table_name text := 'delinquent';
begin
truncate table output1;
insert into output1
execute concat('select * from ',table_name);
end; $$;
select * from output1;
Anybody has an idea on what is wrong and what to do about it?
Many thanks,
You need to run the complete INSERT statement as dynamic SQL. And to build dynamic SQL, using format() is highly recommended to properly deal with identifiers and literals:
do $$
declare
table_name text := 'delinquent';
some_value text := 'a1234';
begin
truncate table output1;
execute format('insert into output1 select * from %I where some_column = %L',
table_name, some_value);
end; $$;
select *
from output1;

Syntax error in defining a stored procedure in DB2

I'm defining a simple stored procedure in DB2 as follows but it gives me syntax error
CREATE OR REPLACE PROCEDURE Schema1.TESTSP1 ()
DYNAMIC RESULT SETS 1
P1: BEGIN
if( exists(
select 1 from syscat.tables where tabschema = 'Schema' and tabname = 'SPTEST'
)) then
drop table Schema.SPTEST ;
create table Schema.SPTEST as
(select * from Schema.XYZ) WITH DATA ;
end if;
END P1
What is wrong here?
You need to study the IBM example stored procedures for SQL PL.
Get the sample procedures working in your environment to build knowledge and skills.
You need to understand the difference between dynamic-sql and static-SQL and when to use either one.
You need to add exception handlers for any exceptions.
As you specified that it returns one result-set you need to open a cursor on that final result-set.
The examples below are for the Db2-LUW Version 11.1.3.3 CLP (command line processor), i.e. on Microsoft Windows that is the what runs when you open db2cwadmin.bat, and on Linux/Unix the bash/ksh shell:
Note: the WITH DATA requires Db2-LUW 11.1.3.3, older versions supported only WITH NO DATA. If you cannot upgrade, then use instead 'CREATE TABLE ... LIKE ...' followed by a separate statement INSERT INTO ... SELECT ...FROM ...
Here is a skeleton resembling your example, using static SQL on Db2-LUW V11.1.3.3:
--#SET TERMINATOR #
CREATE OR REPLACE PROCEDURE Schema1.TESTSP1 ()
DYNAMIC RESULT SETS 1
P1: BEGIN
DECLARE SQLCODE integer;
DECLARE table_exists integer default 0;
select 1 into table_exists from syscat.tables where tabschema = 'SOMESCHEMA' and tabname = 'SPTEST';
if table_exists = 1
then
drop table SOMESCHEMA.SPTEST ;
create table SOMESCHEMA.SPTEST as (select * from SCHEMA.SPTEST ) with data ;
end if;
END P1
#
Here is a skeleton which resembles your example but uses dynamic-SQL on Db2-LUW V11.1.3.3, which would be useful if you don't know in advance the table/column names and they are available in parameters to the stored procedure (not shown) in which case you can dynamically build the SQL and execute it:
--#SET TERMINATOR #
CREATE OR REPLACE PROCEDURE Schema1.TESTSP1 ()
DYNAMIC RESULT SETS 1
P1: BEGIN
DECLARE SQLCODE integer;
DECLARE table_exists integer default 0;
select 1 into table_exists from syscat.tables where tabschema = 'SOMESCHEMA' and tabname = 'SPTEST';
if table_exists = 1
then
execute immediate('drop table SOMESCHEMA.SPTEST') ;
execute immediate('create table SOMESCHEMA.SPTEST as (select * from SCHEMA.SPTEST ) with data') ;
end if;
END P1
#

Error: a column definition list is required for functions in dblink using PostgreSQL 9.3

I have the following function:
In which I am updating one database table by joining other database table by using the dblink().
I have installed:
create extension dblink;
The more details as shown below:
CREATE OR REPLACE FUNCTION Fun_test
(
Table_Name varchar
)
RETURNS void AS
$BODY$
DECLARE
dynamic_statement varchar;
BEGIN
perform dblink_connect('port=5234 dbname=testdb user=postgres password=****');
dynamic_statement := 'With CTE AS
(
Select HNumber,JoiningDate,Name,Address
From '|| Table_Name ||'c
)
, Test_A
AS
(
Select Row_Number() over ( Partition by PNumber order by Date1 Desc,Date2 Desc) AS roNum,
Name,PNumber,Date1,Address
From dblink(
''Select distinct PNumber,
(
case when fname is null then '' else fname end || '' ||
case when lname is null then '' else lname end
) as FullName,
Address,
Date1,Date2
From testdb_Table
inner join CTE on CTE.HNumber = PNumber''
) Num
)
Update CTE
Set
Name = Test_A.FullName
,SubAddress_A = Test_A.Address
,Date1 = Test_A.Date1
from CTE
left outer join Test_A on
CTE.HNumber= Test_A.PNumber
where roNum =1';
RAISE INFO '%',dynamic_statement;
EXECUTE dynamic_statement;
perform dblink_disconnect();
END;
$BODY$
LANGUAGE PLPGSQL;
Calling Function:
select fun_test('test1');
Getting an error:
ERROR: a column definition list is required for functions returning "record"
LINE 11: From dblink
^
You have to tell PostgreSQL what the columns the dblink query will return are.
See the manual for dblink for details.
This is the same as for any function returning a runtime-determined record type. You can't query it without telling PostgreSQL what the column layout of the results will be.
You use a column specifier list, e.g.
SELECT * FROM my_function_returning_record() f(col1 text, col2 integer);
If you are on a current PostgreSQL version you may want to look at postgres_fdw as an alternative to dblink.

ColdFusion CFQuery block as per postgres

I have not been able to get this code block to run my query against postgres in CFQuery of ColdFusion:
<cfquery name="uiCustomColumn" datasource="#arguments.dsn#">
DECLARE resultValue int;
DECLARE nextId bigint;
BEGIN
IF (( select count( udc_id ) from user_defined_column WHERE udc_is_active = true ) >= 10) THEN
INSERT INTO user_defined_column(udc_id)
VALUES(<cfqueryparam value="#this.getLabel()#" cfsqltype="cf_sql_varchar" maxlength="25">)
END IF;
END;
</cfquery>
What you have there is plpgsql syntax (the default PostgreSQL procedural language), not SQL.
You'd need to wrap this in a DO command or CREATE FUNCTION with it.
Or rewrite it with SQL syntax. Something along these lines:
INSERT INTO user_defined_column(udc_id)
SELECT <this.getLabel()>
WHERE (
SELECT count(udc_id) > 9
FROM user_defined_column
WHERE udc_is_active
)