how to combine two sql statements into a function - function

I am trying to combine the following two sql statements in my application code into a function in postgresql, but I'm having some trouble.
Here are the two sql queries I'd like to combine:
UPDATE userrange f
SET UsedYesNo = true, user_id=user_id, updateddatetime=now()
WHERE f.uservalue IN(
SELECT a.uservalue FROM userrange a WHERE UsedYesNo=false Order By id ASC Limit 1)
RETURNING a.uservalue;
The results from the above statement are used in this query:
INSERT INTO widget
VALUES(DEFAULT, uservalue, 'test','123456778',1,"testservername", now(), Null)
So far, I've built function that just does the first update statement, like so:
CREATE or REPlACE FUNCTION create_widget(IN user_id integer, IN password character varying DEFAULT NULL::character varying)
RETURNS TABLE(uservalue integer) AS
$BODY$
BEGIN
RETURN QUERY
UPDATE userrange f SET UsedYesNo = true, user_id=user_id, updateddatetime=now()
WHERE f.uservalue IN(
SELECT a.uservalue FROM userrange a WHERE UsedYesNo=false Order By id ASC Limit 1)
RETURNING a.uservalue;
END;
$BODY$
LANGUAGE plpgsql VOLATILE
It compiles but when I execute it, it fails with the error:
ERROR: missing FROM-clause entry for table "a" LINE 4: RETURNING a.uservalue
I'm just googling this error to see how I can fix it... but could I just create a variable
called uservalue in a DECLARE section and use it in the secondary query? Or can i combine the sql into one
thanks.

If you can forego the function and you have PostgreSQL 9.2+, you can do the update and insert in a single query. It should be straightforward to port this to a function if necessary.
WITH f AS (
UPDATE userrange f
SET UsedYesNo = true, user_id=user_id, updateddatetime=now()
WHERE UsedYesNo IS FALSE
RETURNING f.uservalue)
INSERT INTO widget (<column list>)
SELECT f.uservalue, 'test','123456778',1,"testservername", now(), NULL
FROM f;
-- [edit: added function]
-- Note: the following function is untested
CREATE OR REPLACE FUNCTION create_widget(IN p_user_id INTEGER, IN p_password VARCHAR DEFAULT NULL::VARCHAR)
RETURNS TABLE(uservalue integer) AS
$BODY$
BEGIN
RETURN QUERY
WITH f AS (
UPDATE userrange f
SET UsedYesNo = true,
user_id = p_user_id,
updateddatetime = now()
WHERE UsedYesNo IS FALSE
RETURNING f.uservalue)
INSERT INTO widget (<column_list>)
/* Omit the DEFAULT, by not including it in the column list above,
* the DEFAULT already defined on the column will be used.
*/
SELECT f.uservalue, 'test','123456778',1,"testservername", now(), NULL
FROM f;
END;
$BODY$
LANGUAGE plpgsql VOLATILE;

Related

How to return boolean based on number of records in database?

Here's what I've tried. My host is returning an error, "Sorry an unexpected error happened!" .
I want it to return true if there is at least 1 record with combination pdriver_id, ptruck_number, and pdate.
DELIMITER %%
CREATE FUNCTION DriverActiveInTruckByDate(
pdriver_id INT,
ptruck_number INT,
pdate DATETIME
)
RETURNS boolean
DETERMINISTIC
BEGIN
DECLARE inDB INT DEFAULT 0;
SET inDB =
SELECT IF(COUNT(*) >= 1,1,0)
FROM
truck_timeline tl
WHERE 1=1
AND tl.driver_id = pdriver_id
AND tl.truck_number = ptruck_number
AND ((pdate BETWEEN tl.begin_date AND tl.end_date) OR (pdate >= tl.begin_date AND tl.end_date IS NULL))
END
%%
DELIMITER ;
Several fixes are needed:
The function is not DETERMINISTIC. This means the result will always be the same given the same inputs. In your case, the result may be different depending on the data in your truck_timeline table. So I would suggest using READS SQL DATA.
If you use SET variable = SELECT... you must put the SELECT in a subquery:
SET inDB = (SELECT ...);
The current manual recommends using SELECT ... INTO variable instead of SET. See https://dev.mysql.com/doc/refman/8.0/en/select-into.html
The INTO position at the end of the statement is supported as of MySQL 8.0.20, and is the preferred position.
SELECT ... INTO inDB;
The function you show doesn't have a RETURN statement. See https://dev.mysql.com/doc/refman/8.0/en/return.html
There must be at least one RETURN statement in a stored function.
Your Full Code could be like this:
DELIMITER %%
CREATE FUNCTION DriverActiveInTruckByDate(
pdriver_id INT,
ptruck_number INT,
pdate DATETIME
)
RETURNS boolean
DETERMINISTIC
BEGIN
DECLARE inDB INT DEFAULT 0;
SET inDB =
(SELECT IF(COUNT(*) >= 1,1,0)
FROM
truck_timeline tl
WHERE 1=1
AND tl.driver_id = pdriver_id
AND tl.truck_number = ptruck_number
AND ((pdate BETWEEN tl.begin_date AND tl.end_date) OR (pdate >= tl.begin_date AND tl.end_date IS NULL))
);
END %%
DELIMITER ;

I Getting error when run this below query : RETURN statements in scalar valued functions must include an argument

I am getting this error message
RETURN statements in scalar valued functions must include an argument
when run this query:
create function gender(#gender nvarchar(40))
returns nvarchar(40)
as
begin
(select name,cast(DOB as date) as DOB from datenames where gender = #gender)
return
end
The write way to create a function in mysql for your example is as follows:
DELIMITER \\
create function gender(Igender nvarchar(40))
returns nvarchar(40)
begin
DECLARE customerLevel NVARCHAR(40);
IF EXISTS (select name,cast(DOB as date) as DOB from datenames where gender = Igender) THEN
SET customerLevel = 'SOMETHING1';
ELSE
SET customerLevel = 'SOMETHING2';
END IF;
RETURN (customerLevel);
end
No need to as
No need to # before input
You need to return something.
Don't forget to use DELIMITER.
If you use phpmyadmin and has problem with nvarchar read this post: Unrecognize data type nvarchar in database or simply change it to to varchar.

Can I use subquery in a user-defined function

I try to use subquery in mysql custom user-defined function I get an error so could u help me with one example.
Here is my code:
CREATE DEFINER=`root`#`localhost` FUNCTION `findsubName`(counts INT)
RETURNS varchar(255) CHARSET utf8
BEGIN
DECLARE result VARCHAR(500) DEFAULT NULL;
DECLARE v_name VARCHAR(200);
DECLARE finished INT(1) DEFAULT 0;
DECLARE my_cursor CURSOR FOR
SELECT id, (SELECT t_name FROM ajctb_titles b WHERE a.jt_id=b.t_id)
as tableName FROM ajctb_vacancies a limit counts;
DECLARE CONTINUE HANDLER
FOR NOT FOUND
SET finished = 1;
OPEN my_cursor;
calc_change: LOOP
FETCH my_cursor INTO v_name;
IF finished THEN
LEAVE calc_change;
END IF;
IF result<>'' THEN
SET result = CONCAT_WS(',',result,v_name);
ELSE
SET result = v_name;
END IF;
END LOOP calc_change;
CLOSE my_cursor;
RETURN result;
END
Error message:
Error Code: 1328. Incorrect number of FETCH variables
Error message: Error Code: 1328. Incorrect number of FETCH variables
Error messages attempt to tell you what the problem is. It is in the FETCH. Looking at the documentation:
13.6.6.3 Cursor FETCH Syntax
FETCH [[NEXT] FROM] cursor_name INTO var_name [, var_name] ...
This statement fetches the next row for the
SELECT statement associated with the specified cursor (which must be
open), and advances the cursor pointer. If a row exists, the fetched
columns are stored in the named variables. The number of columns
retrieved by the SELECT statement must match the number of output
variables specified in the FETCH statement.
https://dev.mysql.com/doc/refman/8.0/en/fetch.html
2 columns in your query:
SELECT
id
, (
SELECT
t_name
FROM ajctb_titles b
WHERE a.jt_id = b.t_id
)
AS tableName
means 2 variables are needed the FETCH
It hasn't even attempted the subquery yet.
Regarding that correlated subquery it could be a problem. When you use a subquery in the select clause like this it MUST return no more then one value. So you should use limit 1 if you continue with that subquery.
That subquery can be replaced with a join. e.g.
SELECT
id
, b.t_name AS tableName
FROM ajctb_vacancies a
LEFT JOIN ajctb_titles b ON a.jt_id = b.t_id
You may want to use an INNER JOIN if you must always have a non-null tablename returned.

MySQL Use table name for function

When we use a statement like select count(*) from TABLE, the function count() automatically knows which table it is counting. Is it possible to grab the table and use it in a user defined function.
drop function if exists related_count;
create function related_count(parent int(11)) returns int(11) deterministic
begin
declare count int(11) default 0;
set count=(select count(*) from TABLENAME where id=parent);
return count;
end;
So that I can use it like this:
select count(*),related_count(id) from TABLENAME
So that I can use the same function regardless of table instead of defining multiple functions because of multiple tables.
Is there a way to switch between select count(*) from TABLENAME1 where id=parent or select count(*) from TABLENAME2 where id=parent dependent on a variable related_count('TABLE1',id)
The comment above from #RajeevRanjan mentions using dynamic SQL. This won't work, but if it did it would look like this:
create function related_count(tablename varchar(64), parent int) returns int reads sql data
begin
declare count int default 0;
set #sql = concat('select count(*) into count from `', tablename, '` where id = ', parent);
prepare stmt from #sql;
execute stmt;
return count;
end
However, this is not allowed:
ERROR 1336 (0A000): Dynamic SQL is not allowed in stored function or trigger
The reason it doesn't work is that your stored function could be called by an expression in an SQL statement that is itself a dynamic SQL execution. I guess MySQL only allows one level "deep" of prepare/execute. You can't make a prepared query run another prepared query.
To do this, you'd have to hard-code each table name like the following:
create function related_count(tablename varchar(64), parent int) returns int reads sql data
begin
declare count int default null;
if tablename = 'foo' then set count = (select count(*) from foo where id = parent);
elseif tablename = 'bar' then set count = (select count(*) from bar where id = parent);
elseif tablename = 'baz' then set count = (select count(*) from baz where id = parent);
end if;
return count;
end
This also has an advantage that it isn't an SQL injection vulnerability, whereas the PREPARE/EXECUTE solution (if it had worked) would be.
PS: A function that reads from other tables is not deterministic.

Mysql function returning a value from a query

i want to create a function which calculates a value using a query and I am having a problem returning the value:
Shortened, my query is:
CREATE FUNCTION func01(value1 INT , monto DECIMAL (10,2)) RETURNS DECIMAL(10,2)
BEGIN
SET #var_name = 0;
select #var_name=if(value1 = 1,monto * table.divisa_dolar,table.monto *divisa_euro) from table where data_init = 1;
return #var_nam;
END
I get a SQL syntax error.
SQL Error (1064): You have an error in your SQL syntax;
Assuming these are all generic names (table will not be a good table name), the problem is you can't use == for comparison. You are also missing some key syntax (DECLARE, SELECT INTO, etc.).
Change to this:
CREATE FUNCTION func01(value1 INT , monto DECIMAL (10,2))
RETURNS DECIMAL(10,2)
DETERMINISTIC
BEGIN
DECLARE var_name DECIMAL(10,2);
SET var_name = 0;
SELECT if(value1 = 1,monto *divisa_dolar,monto *divisa_euro) INTO var_name
FROM table
WHERE data_init = 1;
RETURN var_name;
END
MySQL Comparison Functions and Operators
Related Question: Single Equals in MYSQL
Function Help: http://www.databasejournal.com/features/mysql/article.php/3569846/MySQL-Stored-Functions.htm