Dynamic SQL using DB2 - db2

I have been researching this for a few days now and can not specifically find an answer to my issue. I want to be able to dynamically create sql statements to run against my DB2 database. Below is some test code I have playing with to see how the DB2 interprets my sql, but I get an error every time I run this. Please help.
Begin
Declare Monthcol integer;
Declare TXPage integer;
Declare TXYear integer;
Declare text varchar(2000);
set Monthcol = 11;
set TXPage = 10190;
set TXYear = 2018;
set text = 'Select GLMN'|| Monthcol || 'from gldbfa.glpgl where glyear =
2018 and glpage = 10190';
Print text;
end;
I have tried casting the variables to varchar and I have tried moving the print section to after the End. I get
"SQL State: 42601 Vendor Code: -104 Message: [SQL0104] Token TEXT was
not valid. Valid tokens: :. Cause . . . . . : A syntax error was
detected at token TEXT. Token TEXT is not a valid token."
if print is before end.
I get
"SQL State: 42601 Vendor Code: -104 Message: [SQL0104] Token PRINT was
not valid. Valid tokens: ( CL END GET SET CALL DROP FREE HOLD LOCK
OPEN WITH ALTER. Cause . . . . . : A syntax error was detected at
token PRINT."
if print is after the End.
This is super easy to do in SQL Server, but I am very new to DB2. Thank you.

I know this may not be an ideal solution but one workaround would be to use case logic on all twelve rows as follows:
SELECT
CASE
WHEN :Monthcol = 1 THEN GLMN01
WHEN :Monthcol = 2 THEN GLMN02
---
--- etc
---
WHEN :Monthcol = 12 THEN GLMN12
END AS "GLMN"
FROM gldbfa.glpgl
WHERE glyear = :TXYear AND glpage = :TXPage

If the example code you posted is copy/pasted from your actual code then your problem is probably just the missing spaces in your concatenation.
Begin
Declare Monthcol integer;
Declare TXPage integer;
Declare TXYear integer;
Declare text varchar(2000);
set Monthcol = 11;
set TXPage = 10190;
set TXYear = 2018;
set text = 'Select GLMN' || Monthcol || ' from gldbfa.glpgl
where glyear = 2018 and glpage = 10190'; <----- You were missing a space after Monthcol so your select statements was 'Select GLMN11from gldbfa...'
-- execute statment here
end;

Db2 for IBM i does not have a print
You need to be looking at the Db2 for IBM i docs, not Db2 for LUW.
https://www.ibm.com/support/knowledgecenter/ssw_ibm_i_72/rzahg/rzahgsql.htm
In answer to your comment, yes you can create dynamic queries...
Howver, doing so inside an SQL Stored procedure can be tricky...all depends on what you want to do with the results. See this question...
Refer to table name dynamically in DB2/400 SQL query..?
What do you want to do with the row returned?

Related

Can anybody please assist with syntax in a function in postgresql (from mysql)

I am trying to create the following function in PostgreSQL but get the following error. This is from a MySQL procedure that I need to convert to PostgreSQL. I am failing to convert the syntax to PostgreSQL. I am a beginner in PostgreSQL. Please assist me.
CREATE OR REPLACE FUNCTION public.usp_failed_analyze4()
RETURNS TABLE(status varchar) as
$BODY$
SET #maxdate = (SELECT MAX(analyzetime) FROM wp_analyze_history);
SET #maxdateint = (SELECT DATEDIFF(NOW() ,MAX(analyzetime)) FROM wp_analyze_history);
SET #STATUS = SELECT Status from wp_analyze_history WHERE Status NOT IN ('OK','Table is already up to date','The Analyze task DID NOT run!') AND analyzetime = #maxdate);
SET #STATUSNOTRUN = 'The Analyze task DID NOT run!';
IF #maxdateint > 7
THEN SELECT #STATUSNOTRUN;
ELSE SELECT #STATUS as "";
$BODY$
LANGUAGE sql;
error: ERROR: syntax error at or near "#"
Position: 109
It's hard to tell what you want as you tried to copy the MySQL 1:1.
However, there are several problems in your code:
language sql does not have variables or IF statements. You need to use PL/pgSQL (language plpgsql)
PL/pgSQL requires a declare block to declare all variables and the actual code needs a begin ... end; block as well.
You can use SET for assignment
To store the result of a single row query in a variable use select ... into ... from
The character # is invalid in an SQL identifier and can't be used for variable names (which follow the rules of SQL identifiers). In Postgres it's a common habit to prefix variable with something to avoid ambiguity with column names. I use l_ for local variables (but that's completely a personal preference)
You don't seem to want to return multiple rows, but a single value. So you don't need returns table
To return something from a function, use return not select
Putting that all together it should look something like this:
CREATE OR REPLACE FUNCTION usp_failed_analyze4()
RETURNS varchar -- return a single value
AS
$BODY$
declare
l_maxdate timestamp;
l_maxdatediff interval;
l_status text;
l_statusnotrun text;
begin
select MAX(analyzetime), current_timestamp - MAX(analyzetime)
into l_maxdate, l_maxdatediff
FROM wp_analyze_history;
SELECT Status
into l_status
from wp_analyze_history
WHERE Status NOT IN ('OK','Table is already up to date','The Analyze task DID NOT run!')
AND analyzetime = l_maxdate;
l_statusnotrun := 'The Analyze task DID NOT run!';
IF l_maxdatediff > interval '7 days'
THEN
return l_statusnotrun;
ELSE
return ''; -- strings are enclosed in single quotes in SQL
end if;
end;
$BODY$
LANGUAGE plpgsql;
There is still room for a lot of optimization, but this matches your initial code as much as possible.

Using a select in the set of an update pgsql

Hello i'm wondering why I can't use an SELECT in my SET of an UPDATE like this :
UPDATE "WU_Users" SET (SELECT "colonnesName" FROM colonnes WHERE "id" = i) = '0/0' WHERE "IDWU_Users" = (SELECT "idUser" FROM query WHERE "id" = i);
I get this error :
erreur de syntaxe sur ou près de « SELECT »
LIGNE 22 : UPDATE "WU_Users" SET (SELECT "colonnesName" FRO...
Apparently you are attempting to derive the column name to be updated from a column in another table. However, parsing an SQL statement requires the structural components (table names, column name, view names, ...) to be fixed before the statement can be parsed. What you are attempting would require parsing part of the statement inside parsing the statement. Currently, that is not going to happen. To accomplish what you are attempting will require dynamic SQL in order to generate the UPDATE statement at run time. Something like:
create or replace
procedure update_wu_users_from_idwu_users(i integer) -- assumes i as parameter as it is not defined
language plpgsql
as $$
declare
k_update_stmt constant text =
$stmt$ update wu_users set %I = '0/0'
where wu_users =
(select iduser
from query -- assumes query is a table, view, or materalized view
where id = i
)
$stmt$;
l_update_column text;
l_update_stmt text;
begin
select colonnesname
into l_update_column
from columns
where id = i;
if not found then
raise exception 'No column name found on table colonnes for id=%',i;
end if;
l_update_stmt = format(k_update_stmt,k_update_stmt);
raise notice '%', l_update_stmt; -- for debigging for prod raise_log might be better
execute l_update_stmt;
end;
$$;
NOTE: In the absence of table definition and test data the above IS NOT tested. Nor will I quarantine it complies. It is offered as example.

Trying to run dynamic sql using a UDF in DB2

I am very new to DB2 even though have experience in Oracle. I am not able to resolve this issue.I have a requirement where I need to find missing child records in the parent table .The parent table , child table and the join_key are all passed as input parameter.
I have tried this in a procedure was able to achieve this, but the admin wants it in a function so that they can just use it in a select statment and get the result in a table format. Since the parent table , child table and the join_key are comming as input parement, I am not able to run them as dynamic sql.
create or replace function missing_child_rec(PARENT_TBL VARCHAR(255),JOIN_KEY VARCHAR(255),CHILD_TBL VARCHAR(255))
RETURNS TABLE(Key VARCHAR(255))
LANGUAGE SQL
BEGIN
DECLARE V_SQL VARCHAR(500);
DECLARE C_SQL CURSOR WITH RETURN FOR S_SQL;
SET V_PARENT_TAB = PARENT_TBL;
SET V_KEY = JOIN_KEY;
SET V_CHILD_TAB = CHILD_TBL;
SET V_SQL = 'SELECT DISTINCT '|| JOIN_KEY || ' FROM ' || V_CHILD_TAB || ' A WHERE NOT EXISTS
(SELECT ' ||V_KEY || ' FROM ' || V_PARENT_TAB || ' B WHERE A.'||JOIN_KEY || '= B.'||JOIN_KEY ||' )' ;
PREPARE S_SQL FROM V_SQL;
OPEN C_SQL;
CLOSE C_SQL;
RETURN
END
When I try to compile it , it says prepare is invalid , I have tried even execute immediate but that also gave error.Can you please help me with how to use dynamic sql in UDF or an alternative logic for this problem
There is more than one way to solve this, here's one way.
If you already have a working stored-procedure that returns the correct result-set then you can call that stored-procedure from a pipelined table function. The idea is that a pipelined table function can consume the result-set and pipe it to the caller.
This will work on Db2-LUW v10.1 or higher, as long as the database is not partitioned over multiple nodes.
It may work on Db2-for-i v7.1 or higher.
It will not work with Db2 for Z/os at current versions.
Suppose your stored procedure is sp_missing_child_rec and it takes the same input parameters as the function you show in your question, and suppose the data type of the join column is varchar(100).
The pipelined wrapper table function would look something like this:
--#SET TERMINATOR #
create or replace function missing_child_rec(PARENT_TBL VARCHAR(255),JOIN_KEY VARCHAR(255),CHILD_TBL VARCHAR(255))
returns table ( join_column_value varchar(100))
begin
declare v_rs result_set_locator varying;
declare v_row varchar(100); -- to match the join_column_datatype, adjust as necessary
declare sqlstate char(5) default '00000';
CALL sp_missing_child_rec( parent_tbl, join_key, child_tbl);
associate result set locator (v_rs) with procedure sp_missing_child_rec ;
allocate v_rscur cursor for result set v_rs;
fetch from v_rscur into v_row;
while ( sqlstate = '00000') do
pipe(v_row);
fetch from v_rscur into v_row;
end while;
return;
end#
select * from table(missing_child_rec( 'parent_table' , 'join_column', 'child_table'))
#

How do convert below code from T-SQL into DB2 LUW?

How do I convert this code from T-SQL into DB2 LUW, it seems so easy with T-SQL but in DB2 can't find any solution. See code below:
DECLARE #sqlCommand varchar(1000)
DECLARE #columnList varchar(75)
DECLARE #city varchar(75)
SET #columnList = 'AddressID, AddressLine1, City'
SET #city = '''London'''
SET #sqlCommand = 'SELECT ' + #columnList + ' FROM Person.Address WHERE City = ' + #city
EXEC (#sqlCommand)
The problem is that you can’t ‘select to nowhere’ in a compound statement in DB2. Db2 CLP can return you the result set of a single sql statement, but it doesn’t try to do the same for select statements in a compound statement. If you want to print the result set from a select statement in a compound statement, you can, for example, declare a cursor, fetch it in a loop, and use dbms_output.put_line calls to print the values of variables.
Not Pretty but you can find an example of the bottom of this page:
Stored Procedures and Dynamic SQL Returning a Result set
Essentially you most:
1) create a dynamic SQL string
2) prepare the string into a statement
3) Link the statement to a cursor you're going to declare as WITH RETURN
Opening the cursor will be the last line in your procedure.

Global temporary table and bulk collect

i'm gonna to explain you my problem.
I have to convert a t-sql script into a pl/sql script.
There is my code in t-sql :
CREATE TABLE #temp_tb ( temp_row nvarchar(max))
if ##error <> 0 goto lbl_end
DECLARE #bulk_cmd varchar(1000)
set #bulk_cmd = 'BULK INSERT #temp_tb FROM ''c:\Communication\Test\MSG_IN\'+'$(nomfic)'+''' WITH
(
CODEPAGE = ''RAW'',
ROWTERMINATOR = '''+CHAR(10)+''',
DATAFILETYPE = ''WIDECHAR''
)'
exec (#bulk_cmd)
if ##error <> 0 goto lbl_end
And after a lot of oracle doc and forum, i write this :
CREATE GLOBAL TEMPORARY TABLE temp_tb ( temp_row NCHAR(MAX)) ON COMMIT DELETE ROWS;
BEGIN
EXCEPTION
WHEN OTHERS THEN GOTO lbl_end;
END;
DECLARE bulk_cmd varchar2(1000);
bulk_cmd := 'BULK INSERT temp_tb FROM ''c:\Communication\Test\MSG_IN\'+'$(nomfic)'+''' WITH
(
CODEPAGE = ''RAW'',
ROWTERMINATOR = '''+CHAR(10)+''',
DATAFILETYPE = ''WIDECHAR''
)';
BEGIN
exec (bulk_cmd);
EXCEPTION
WHEN OTHERS THEN GOTO lbl_end;
END;
But, i've an error here NCHAR(MAX). I've to put a large value like 100000 ? If i put this, he retunrs me an error after the EXECEPTION Syntax error...
In other hand, if someone can say me how to adapt me bulk collect... I don't find any doc on bulk collect to integrated file like me...
I hope i'm clear, because it's really difficult for me to explain... I'm beginner in Oracle(PL/SQL)...
Thank's for help guys, again
EDIT : I launch this script by a .bat. I took parameters, that's filled '$(nomfic)' with the values in function of the parameters.