2620:Bad character error in stored procedure teradata - stored-procedures

Concatenation is working in select statement:
SELECT 'HELLO' || 'WORLD';
is returning HELLOWORLD, but when I try to use it in stored procedure like below:
SET Time_of_Day=TRIM(Hour_of_Day) || ' : ' || TRIM(Minute_of_Hour) || ' : ' || TRIM(Second_of_Minute);
where Hour_of_Day,Minute_of_Hour,Second_of_Minute are variables, I also tried without TRIM:
ERROR:
**CALL FAILED 2620:PROCEDURE_NAME:THE FORMAT OR DATA CONTAINS A BAD CHARACTER**

Casting a string to a time in Teradata requires two-digit hour/minute/second:
SET Time_of_Day=TRIM(Hour_of_Day (FORMAT '99')) || ' : ' ||
TRIM(Minute_of_Hour (FORMAT '99')) || ' : ' ||
TRIM(Second_of_Minute (FORMAT '99'))
But this snippet is probably from the SP question you deleted an hour ago (a few seconds before I could post my answer).
There's no need for running 86400 single-row Inserts, simply create all data in a single Select, e.g.:
SELECT
Row_Number() Over (ORDER BY h,m,s),
Extract(SECOND From t) AS s,
Extract(MINUTE From t) AS m,
Extract(HOUR From t) AS h,
t
FROM
(
SELECT Cast(Begin(pd) AS TIME(0)) AS t
FROM sys_calendar.CALENDAR
WHERE calendar_date = Current_Date
EXPAND ON PERIOD(Cast(Current_Date AS TIMESTAMP(0)), Cast(Current_Date + 1 AS TIMESTAMP(0))) AS pd
) AS dt

Related

for loop with dynamic table name and execute immediate

in my Procedure there is the following code line
for i in (select schema_name, table_name, restricted_columns
from GRANTED_TABLES_FOR_ROLE
where restricted_columns = 0) loop
execute immediate 'grant select on ' || i.schema_name || '.' || i.table_name || ' to ROLE_NAME';
end loop;
because i want to create the table "GRANTED_TABLES_FOR_ROLE" earlier in my procudere i can't create the procedure without the "GRANTED_TABLES_FOR_ROLE" existing.
is there any way to make the code above dynamic so i can set a variable for the table "GRANTED_TABLES_FOR_ROLE"?
how i can achieve this?
thanks for your help!
I believe this is a case where you will need to use a dynamic cursor:
DECLARE
TYPE trec IS RECORD
(
schema_name VARCHAR2 (30)
, table_name VARCHAR2 (30)
, restricted_columns VARCHAR2 (30)
);
l_rec trec;
l_sqlstment VARCHAR2 (500)
:= q'[SELECT schema_name, table_name, restricted_columns
FROM <<tablename>>
WHERE restricted_columns = 0 ]';
l_cursor SYS_REFCURSOR;
BEGIN
l_sqlstment :=
REPLACE (l_sqlstment, '<<tablename>>', 'granted_tables_for_role');
OPEN l_cursor FOR l_sqlstatement;
LOOP
FETCH l_cursor INTO l_rec;
EXIT WHEN l_cursor%NOTFOUND;
dbms_outout.put_line (l_rec.schema_name);
dbms_outout.put_line (l_rec.table_name);
dbms_outout.put_line (l_rec.restricted_columns);
EXECUTE IMMEDIATE 'grant select on '
|| l_rec.schema_name
|| '.'
|| l_rec.table_name
|| ' to ROLE_NAME';
END LOOP;
END;

Remove leading zeros from string timestamp

Lua supports limited Expressions and Patterns
The time stamp is variable in the format DD:HH:MM:SS,FF (Days:Hours:Minutes:Seconds,Miliseconds)
The objective is to trim/truncate the timestamp by removing any leading zeros to represent as follow:
Example timestamps includes:
timestamp = 00:00:00:00,00 => 0
timestamp = 00:01:00:00,00 => 1:00:00,00
timestamp = 00:00:01:00,00 => 1:00,00
timestamp = 00:00:00:01,00 => 1,00
timestamp = 99:23:59:59,99 => 99:23:59:59,99
I am looking for a simple solution such as using gsub, TA
This regexp gets 5/5 but it is soo long
local reg = '^[0]+[:]?[0]+[:]?[0]+[:]?[0]?'
print('1 > ' .. '00:00:00:00,00 = ' .. ('00:00:00:00,00'):gsub(reg,''))
print('2 > ' .. '00:01:00:00,00 = ' .. ('00:01:00:00,00'):gsub(reg,''))
print('3 > ' .. '00:00:01:00,00 = ' .. ('00:00:01:00,00'):gsub(reg,''))
print('4 > ' .. '00:00:00:01,00 = ' .. ('00:00:00:01,00'):gsub(reg,''))
print('5 > ' .. '99:23:59:59,99 = ' .. ('99:23:59:59,99'):gsub(reg,''))
Why not use string.match? The below code is based on the assumption that the input will be a valid timestamp. It handles all five cases correctly, but I'm not sure whether trimming something like 00:00:00:00,42 to 42 - which this does - is desired.
local function trim_timestamp(timestamp)
return timestamp:match"^[0:,]*(.+)$"
end
Otherwise, move the comma and the first digit of the seconds to the captured part:
local function trim_timestamp(timestamp)
return timestamp:match"^[0:]*(%d,.+)$"
end
Note that this will then turn the first example into 0,00; you'd have to explicitly handle the case of all zeroes:
local function trim_timestamp(timestamp)
local trimmed = timestamp:match"^[0:]*(%d,.+)$"
if trimmed == "0,00" then return "0" end -- shorten all zeroes
return trimmed
end

Variable Assignment Issue for Multiple dynamic SQL in DB2 Iseries

We are using DB2 Iseries V7R3 on AS400 system.
In one of the stored procedure, we are preparing dynamic SQL queries. Each SQL query is assigned to different variables. When we execute the stored procedure sometimes it fails but when retry with same parameters it works.
Upon putting logs in the stored procedure, we have observed that during the failed cases value used for variable 2 is from variable 1.
Attached is the stored procedure and screenshot of the logs.
Appreciate any help on this, running out of the thinking options for this.
However, sometimes it uses select * for variable1 as well. After retry it works ok.
create stored procedure (
)DYNAMIC RESULT SETS 1
LANGUAGE SQL
SPECIFIC SYMDTA.PRC_RETRIEVE_CLAIM_LIST
NOT DETERMINISTIC
MODIFIES SQL DATA
CALLED ON NULL INPUT
COMMIT ON RETURN YES
CONCURRENT ACCESS RESOLUTION USE CURRENTLY COMMITTED
SET OPTION ALWBLK = *ALLREAD ,
ALWCPYDTA = *OPTIMIZE ,
COMMIT = *NONE ,
DECRESULT = (31, 31, 00) ,
DYNDFTCOL = *NO ,
DYNUSRPRF = *USER ,
SRTSEQ = *HEX
BEGIN
DECLARE DATACLAIM CLOB ( 1048576 ) DEFAULT ' ' ;
DECLARE GCLAIMCOUNT CLOB ( 1048576 ) DEFAULT ' ' ;
DECLARE CR_CLAIM_LIST_STMT CURSOR WITH HOLD FOR CLM_DATA_STMT ;
DECLARE CR_CLAIM_COUNT_STMT CURSOR WITH HOLD FOR CLM_COUNT_STMT ;
SET DATACLAIM = 'SELECT * FROM table ';
SET GCLAIMCOUNT = 'select count(*) from table';
INSERT INTO DEBUGGING_DYNAMIC_QUERIES VALUES ( POLICY_NO , DATACLAIM , CURRENT TIMESTAMP , 'DATACLAIM' ) ;
INSERT INTO DEBUGGING_DYNAMIC_QUERIES VALUES ( GCLAIMCOUNT , CURRENT TIMESTAMP , 'GCLAIMCOUNT' ) ;
PREPARE CLM_DATA_STMT FROM DATACLAIM ;
OPEN CR_CLAIM_LIST_STMT ;
PREPARE CLM_COUNT_STMT FROM GCLAIMCOUNT ;
OPEN CR_CLAIM_COUNT_STMT ;
FETCH CR_CLAIM_COUNT_STMT INTO TOTAL_RECORDS_G4 ;
CLOSE CR_CLAIM_COUNT_STMT ;
Output the debug table :-
Wrong :-
DATACLAIM = "select * " - 2020-01-01 11:00 AM
GCLAIMCOUNT = "Select * " - 2020-01-01 11:01 AM
After retry :-
DATACLAIM = "select * " - 2020-01-01 12:00 pm
GCLAIMCOUNT = "Select count(*) " - 2020-01-01 12:01 Pm

Stored procedure not compiling with 'Set'

I have a Stored procedure which I am not able to compile.
CREATE PROCEDURE FINGOODSCH(IN STRDATE DATE,
IN prodln Char(5))
LANGUAGE SQL
RESULT SETS 1
SET OPTION DBGVIEW =*SOURCE
BEGIN
Declare IN_DATE NUMERIC(7,0);
SET IN_DATE = 0;
/* SET IN_DATE = (DECIMAL(CHAR(SUBSTR(CHAR(STRDATE),1,4); */
/* SUBSTR(CHAR(STRDATE),6,2) CONCAT */
/* SUBSTR(CHAR(STRDATE),9,2))) - 19000000) ; */
Declare FinGoodSCH Cursor for
Select TRIM(ORDNO) as OrderNumber,
( '20' || SUBSTR(CHAR(ODUDT),2,2) || '-' ||
SUBSTR(CHAR(ODUDT),4,2) || '-' ||
SUBSTR(CHAR(ODUDT),6,2)) as OrderDueDate,
TRIM(FITEM) as ModelNumber,
TRIM(DPTNO) as ProductionLine
From ORMAST
Where
DPTNO = prodln
OPEN FinGoodSCH ;
END
THe issue is withthe statement 'SET IN_DATE = 0; (I know I can use Default to set it to 0, but thats not what I am looking for)'. If I remove this statement, it will compile. Compilation error is:
SQL0104 30 3 Position 33 Token FINGOODSCH was not valid. Valid
tokens: GLOBAL.
Also, I tried declaring it with decimal but it did not work
You have to do the declarations in SQL before any executable code....
CREATE PROCEDURE FINGOODSCH(IN STRDATE DATE,
IN prodln Char(5))
LANGUAGE SQL
RESULT SETS 1
SET OPTION DBGVIEW =*SOURCE
BEGIN
Declare IN_DATE NUMERIC(7,0);
Declare FinGoodSCH Cursor for
Select TRIM(ORDNO) as OrderNumber,
( '20' || SUBSTR(CHAR(ODUDT),2,2) || '-' ||
SUBSTR(CHAR(ODUDT),4,2) || '-' ||
SUBSTR(CHAR(ODUDT),6,2)) as OrderDueDate,
TRIM(FITEM) as ModelNumber,
TRIM(DPTNO) as ProductionLine
From ORMAST
Where
DPTNO = prodln;
-- Executable code starts here....
SET IN_DATE = (DECIMAL(CHAR(SUBSTR(CHAR(STRDATE),1,4);
SUBSTR(CHAR(STRDATE),6,2) CONCAT
SUBSTR(CHAR(STRDATE),9,2))) - 19000000) ;
OPEN FinGoodSCH ;
END
Note that if you happened to want to use IN_DATE in your cursor, you'd still do it like the above. The value of any variables used in the DECLARE CURSOR statement are not evaluated until the cursor is opened in DB2.

Stored procedere: flexible param for operator on 'IF #recordCount <= #Menge'

I hope someone can me help again, regarding to my solved stored procedure problem
Is there any chance to make the operator ">" flexible?
Right now my stored procedure works very fine and I will get an eMail when the record count is bigger than the param Menge.
The code is this:
#Menge as int = 0,
#recordCount as int = 0,
set #MySQL = 'select #recordCount=count(2) from ' + #MyTable + ' where ' + #MyWhere
exec sp_execute #MySQL, N'#recordCount int OUTPUT', #recordCount=#recordCount OUTPUT
IF #recordCount > #Menge
begin
...
EXEC msdb.dbo.sp_send_dbmail
Now I want to make > flexible to get an e-mail when the record count is smaller than menge
I tried to declare the param but I don't know how to insert it into the if #recordcount > #Menge line :(
#OpInd as char(1) = null
I would call the stored procedure with
exec sp_eMail_Test3
#Menge = 0,
#eMail_TO = 'testuser#test.xx' ,
#eMail_Subject = 'test3 ',
#eMail_Body = 'Hallo, das ist ein Test',
#MyTable ='test' ,
#MyWhere = 'not [sys_completed] is null'
#OpInd = '<'
If I try IF #recordCount + #OpInd + #Menge then I get the error
An expression of non-boolean type specified in a context where a condition is expected, near 'begin'.
Hopefully you understand my Target and can help me.
If necessary I need to build a 2nd stored procedure :( one for "<" and one for ">"
Best regards Ralf
Just change your operand to <>
IF #recordCount <> #Menge
begin
...
EXEC msdb.dbo.sp_send_dbmail
There are only 3 possible operands you want to deal with. >, <, and =. Since you want an email if it's > or if it's < then just send the email when it's not =.
Another way would to repeat your code.
IF (#recordCount > #Menge or #recordCount < #Menge)
begin
...
EXEC msdb.dbo.sp_send_dbmail
If you are trying to make it either > or < then you can use DynamicSQL. Something like...
declare #sql varchar(max)
set #sql =
'IF ' + cast(#recordCount as varchar(16)) + ' ' + #OpInd + ' ' + cast(#Menge as varchar(16)) + '
begin
...
EXEC msdb.dbo.sp_send_dbmail'
EXEC(#sql)

Resources