db2 - CASE WHEN or IF Statement in CREATE FUNCTION - function

Hi I've got a problem managing CASE WHEN or IF Statements in a CREATE FUNCTION Call in DB2
I tried this Statement:
CREATE OR REPLACE FUNCTION NAECHSTES_DATUM(PARAM1 CHAR(6), PARAM2 DATE)
RETURNS DATE
LANGUAGE SQL
BEGIN
DECLARE BASEDATE DATE;
DECLARE THATDATE DATE;
SET BASEDATE = TO_DATE(CONCAT(PARAM1,CAST(YEAR(PARAM2) AS CHAR(4))),'DD.MM.YYYY');
IF (BASEDATE >= PARAM2)
THEN SET THATDATE = BASEDATE;
ELSE SET THATDATE = BASEDATE + 1 YEAR;
END IF;
RETURN THATDATE;
END
I get this error
[-104] Auf "+ 1 YEAR" folgte das unerwartete Token "END-OF-STATEMENT". Mögliche Tokens: "
END IF".. SQLCODE=-104, SQLSTATE=42601, DRIVER=4.14.113
Similar result when I use CASE WHEN.
Do you know where the problem could be?

Use an alternative statement delimiter after the END of the function.
Inside the function the statement delimiter is the semicolon (;)
But Db2 needs to know an additional delimiter to indicate the end of the block.
For the Db2 command line (in shell scripts, batch files) you can use the "-td#" command line option and terminate the block with the # character. Inside a file that contains the function you can use:
--#SET TERMINATOR #
anywhere before the function block and then terminate the block with #
If you are use a GUI tool to submit the DDL or SQL, each GUI tool has its own way to specify alternative statement delimiters. Look at the settings and properties. It's always wise to fully describe your toolset (which programs, which versions, which operating system etc) in your question.

Related

MySQL Cursors - having errors in MySQL Workbench

I am trying to write a mysql program, which has cursors in it. Due to an error in writing an DECLARE query, MySQL Workbench is always showing me the DECLARE is not valid at this position, expected EOF, ALTER, ANALYZE, BEGIN, BINLOG, CACHE, ...
Could you help me solve this problem?
Here is my code:
DELIMITER //
BEGIN
declare Naslov_knjige VARCHAR(24);
declare Cena_knjige DECIMAL(8,2);
DECLARE cursor_cene CURSOR
FOR SELECT
Naslov,
Cena
FROM
prvi_test_v2.knjige;
OPEN cursor_cene //
FETCH NEXT FROM cursor_cene INTO
#Naslov_knjige,
#Cena_knjige //
WHILE ##FETCH_STATUS = 0
BEGIN
PRINT #Naslov_knjige + CAST(#Cena_knjige AS VARCHAR) //
FETCH NEXT FROM cursor_cene INTO
#Naslov_knjige,
#Cena_knjige //
END //
CLOSE cursor_cene //
DEALLOCATE cursor_cene //
END //
DELIMITER ;
Thanks for your help!
I will assume you omitted a line for CREATE PROCEDURE, because in MySQL a BEGIN...END block must be part of a stored routine. See https://dev.mysql.com/doc/refman/8.0/en/begin-end.html
BEGIN ... END syntax is used for writing compound statements, which can appear within stored programs (stored procedures and functions, triggers, and events).
You changed the DELIMITER:
DELIMITER //
Using this delimiter terminates the whole CREATE PROCEDURE statement. You should not do this after the first statement in the body of the procedure. You need to use the normal ; terminators for each statement within the body of the procedure. The reason for changing the delimiter is so you can use ; for each statement in the procedure without ending the CREATE PROCEDURE.
See examples and documentation here: https://dev.mysql.com/doc/refman/8.0/en/stored-programs-defining.html
That's the reason for the error you got. You used // to terminate OPEN cursor_cene // which ended the CREATE PROCEDURE, but clearly there was more to that procedure.
There are other problems with your procedure. You seem to be using Microsoft SQL Server syntax, but MySQL is different.
Naslov_knjige is not the same variable as #Naslov_knjige in MySQL. Don't use the # sigil in front of local variables. If you use the # sigil, this refers to a user-defined variable.
The WHILE ##FETCH_STATUS = 0 syntax is specific to Microsoft SQL Server. MySQL has different syntax for running a cursor loop. See example in the documentation: https://dev.mysql.com/doc/refman/8.0/en/cursors.html
That's as far as I got. There may be more problems, but I am not going to look for them.

Can someone help me with functions in MySQL?

I run the following script:
USE MODERN_FAMILY;
DROP FUNCTION IF EXISTS compare_news;
DELIMITER $$
CREATE FUNCTION compare_news(n INT, m INT)
RETURNS VARCHAR(20)
BEGIN
DECLARE s VARCHAR(20);
IF n>m THEN SET s='>';
ELSEIF n=m THEN SET s='=';
ELSE SET s='<';
END IF;
SET s = CONCAT(n, ' ', s, ' ',m);
RETURN s; END;$$
First script returns this error :
Error Code: 1418. This function has none of DETERMINISTIC, NO SQL, or READS SQL DATA in its declaration and binary logging is enabled (you might want to use the less safe log_bin_trust_function_creators variable).
Then, I run this:
USE MODERN_FAMILY;
SELECT compare_news(2,5);
It returns this error:
Error Code: 2014. Commands out of sync; you can't run this command now.
Does someone know if I have an error with the script? Or is related to my SQL configuration?
log_bin_trust_function_creators variable controls whether binary logging should trust the stored function creators for not to create unsafe stored functions.
Reference: Stored Program Logging
When you create a stored function, you must declare either that it is
deterministic or that it does not modify data. Otherwise, it may be
unsafe for data recovery or replication.
By default, for a CREATE FUNCTION statement to be accepted, at least
one of DETERMINISTIC, NO SQL, or READS SQL DATA must be specified
explicitly. Otherwise an error occurs:
ERROR 1418 (HY000): This function has none of DETERMINISTIC, NO SQL,
or READS SQL DATA in its declaration and binary logging is enabled
(you *might* want to use the less safe log_bin_trust_function_creators
variable)
To relax the preceding conditions on function creation (that you must
have the SUPER privilege and that a function must be declared
deterministic or to not modify data), set the global
log_bin_trust_function_creators system variable to 1.
Solution 1: Make your function which doesn't manipulate data or deterministic in nature
CREATE FUNCTION `compare_news`(
`n` INT,
`m` INT
) RETURNS VARCHAR(20) CHARSET latin1 LANGUAGE SQL DETERMINISTIC NO SQL SQL SECURITY DEFINER COMMENT ''
BEGIN
DECLARE s VARCHAR(20);
IF n>m THEN
SET s='>';
ELSEIF n=m THEN
SET s='=';
ELSE
SET s='<';
END IF;
SET s = CONCAT(n, ' ', s, ' ',m);
RETURN s;
END
Solution 2: Enable MySQL to trust such functions by setting mysql log_bin_trust_function_creators variable to ON.
Reference: log_bin_trust_function_creators
SET GLOBAL log_bin_trust_function_creators = 1;
The variable will change upon restart if you do not update the config to reflect the change.

Mysql Regex Search and Replace not updating records

I have a MYSQL database that needs some data removed from a field.
My current data is like this..
gttp://www.somesite.com?ref=1234567
gttp//notquitealink.com/ref.php?r=myreferral
I am trying to remove the gttp://www.somesite.com?ref= to leave only the last part after the =
so I want the data to look like this
1234567
myreferral
I found this code from a previous question. MySQL Regex Search and replace
DELIMITER $$
CREATE FUNCTION `regex_replace`(pattern VARCHAR(1000),replacement VARCHAR(1000),original VARCHAR(1000))
RETURNS VARCHAR(1000)
DETERMINISTIC
BEGIN
DECLARE temp VARCHAR(1000);
DECLARE ch VARCHAR(1);
DECLARE i INT;
SET i = 1;
SET temp = '';
IF original REGEXP pattern THEN
loop_label: LOOP
IF i>CHAR_LENGTH(original) THEN
LEAVE loop_label;
END IF;
SET ch = SUBSTRING(original,i,1);
IF NOT ch REGEXP pattern THEN
SET temp = CONCAT(temp,ch);
ELSE
SET temp = CONCAT(temp,replacement);
END IF;
SET i=i+1;
END LOOP;
ELSE
SET temp = original;
END IF;
RETURN temp;
END$$
DELIMITER ;
I ran it and it created a function that I could query on.
When I run this query....
UPDATE `tb_te_builder_user` SET tebu_refID = regex_replace('gttp(.*)=', '',tebu_refID);
I get 0 rows affected. (Query took 10.9480 sec)
But when I try to do a select
SELECT * FROM `tb_te_builder_user` WHERE tebu_refID REGEXP 'gttp(.*)='
I get well over 7,000 records returned.
I am not quite sure what to make of it... I think I am missing something very simple here.
I was also looking at this solution https://github.com/mysqludf/lib_mysqludf_preg#readme but I don't have rights to use the make command on my server. So I am left with trying to figure out what I am doing incorrectly.
Any help is appreciated.
That function only works for a single character pattern. This limitation is mentioned at the source site.
For a string pattern you may use a user defined function, eg. mysql-udf-regexp, see How to do a regular expression replace in MySQL?. However, to install a user defined function you need to compile it and add it to the MySQL installation.
MariaDB, a MySQL drop-in replacement, has a builtin function regex_replace fully supporting string patterns.
If you cannot do any of these you may try an external solution, eg. using PHP and PHP's preg_replace function.

use of ':=' fails in select in stored procedure due to variable definition

Why does the use of the assignment operator := fail to parse in this stored procedure (fragment)? In the update statement, in the set median = [select expression], in the expression, the MySQL 5.6 parser reports the error, "[Check]...for the right syntax to use near ':= row + 1 as row, $vol as $vol from univ_members' ".
delimiter //
CREATE PROCEDURE m()
BEGIN
DECLARE row int;
SELECT row := row + 1 AS row;
END //
delimiter ;
Running the select statement the mysql shell also fails, but says, 'row' is not a system variable or 'row' is not a column, depending on whether I try to define it with 'set'.
Do you know of a limitation in a stored procedure that prohibits this, or of such a bug in MySQL 5.6? If so, is there a workaround? Can you suggest an alternative approach?
So, after struggling like a man blinded by darkness, I defined the variable #row in the shell using 'set' (the shell's parser does not allow 'row') and it worked. The parser however does not allow a variable defined in a stored procedure with 'declare' to begin with a '#', but, if defined with 'set', it works, it does allow it to be used as the left-hand value in the :=.
So, it's an issue with variable definition. Evidently, only 'user variables', which must begin with a '#' and must be defined with 'set', can be assigned values with ':='. (See User-Defined Variables)
I find such a nuance that all variables don't share a common behavior when it comes to assignment non-intuitive and incredibly irritating. Am I still missing something?

Attempting to write PostgreSQL 9.0 function

I am attempting to write a function in PostgreSQL 9.0. This will eventually be used in a new aggregate function, but one step at a time.
Here is what I have so far:
create or replace function encstate(text,text) returns text as $$
DECLARE
oldstate alias for $1;
arg alias for $2;
BEGIN
IF length(oldstate)>0 then
select 'Encrypted';
else if
select '';
end if;
END;
$$ language sql strict immutable;
(I know I'm not yet using the $2 argument.)
The result is:
ERROR: syntax error at or near "alias"
LINE 3: oldstate alias for $1;
When I remove the DECLARE block and just refer to the arguments as $1 etc in the body, the result is:
ERROR: syntax error at or near "if"
LINE 3: if length($1)>0 then
As far as I can tell, what I have matches examples found on the web, except I could find no examples of functions with an if-statement, so I have no idea what I'm doing wrong. Any help would be appreciated.
I would suggest doing this as an SQL function:
create or replace function encstate(text,text) returns text as $$
SELECT CASE WHEN length($1)>0 then 'Encrypted' ELSE '' END;
$$ language sql strict immutable;
You could also do what you did with the other, but change sql to plpgsql. My suggestion though is that what you can do in an SQL function you should do in one usually. You will get better performance and the planner can do more with it.
If you want a SQL function:
create or replace function encstate(text, text) returns text as $$
select case
when length($1) > 0 then 'Encrypted'
else ''
end
;
$$ language sql strict immutable;
SQL has no variables or control structures as it is not procedural, it is declarative. If you want procedural features then use a plpgsql function:
create or replace function encstate(text, text) returns text as $$
DECLARE
oldstate alias for $1;
arg alias for $2;
BEGIN
IF length(oldstate) > 0 then
return 'Encrypted';
else
return '';
end if;
END;
$$ language plpgsql strict immutable;
SQL
CREATE OR REPLACE FUNCTION encstate(oldstate text, arg text)
RETURNS text LANGUAGE SQL IMMUTABLE AS
$func$
SELECT CASE WHEN $1 <> '' THEN 'Encrypted' ELSE '' END
$func$
PL/pgSQL
CREATE OR REPLACE FUNCTION encstate(oldstate text, arg text)
RETURNS text LANGUAGE plpgsql IMMUTABLE AS
$func$
BEGIN
IF oldstat <> '' THEN
RETURN 'Encrypted';
ELSE
RETURN '';
END IF;
END
$func$;
Major points
The expression length(x) > 0 (x being text) only excludes '' and NULL.
Use the 100 % equivalent expression x <> ''. Does the same simpler and faster, regardless of whether the function is declared STRICT or not.
Don't use plpgsql ALIAS if you don't have to. It's only there for compatibility and to rename pre-determined parameter names. The manual actively discourages its use for other purposes. I never use it. Named parameters are available since version 8.1. Simpler, better.
In SQL functions you can refer to parameter names (instead of positional parameters ($1, $2, ..) since PostgreSQL 9.2. It's still a good idea to name parameters even before that, for documentation.
I suspect you do not want to declare this function STRICT (synonym: RETURNS NULL ON NULL INPUT). Like the synonym implies, that returns NULL on (any) NULL input. Seems like you want an empty string ('') instead.
There is also a performance implication:
Function executes faster without STRICT modifier?