Error creating MYSQL function to work out time diff - mysql

I am creating a function as part of a database project with a group at university. I want the function to return the fastest time a race was complete in and have tried the following code. I am getting an error as I think it is trying to return more than one value but cannot think of another way to do this. Any help is appreciated.
DELIMITER //
CREATE OR REPLACE FUNCTION calc_race_winner(in_race_id INT)
RETURNS FLOAT(10)
BEGIN
CREATE TEMPORARY TABLE IF NOT EXISTS temp_result AS (
SELECT
(TIME_TO_SEC(TIMEDIFF(ps_result.finish_time, ps_race.start_time)) / 60) AS finish_time_mins
FROM ps_race
INNER JOIN ps_result ON ps_race.race_id = ps_result.race_id
);
SELECT *
FROM temp_result
WHERE ps_race.race_id = in_race_id
ORDER BY finish_time_mins ASC LIMIT 1;
RETURN finish_time_mins;
END//
DELIMITER ;

Define a local variable to read finish time into it and return the same.
begin
declare finish_time_calculated_in_mins int default 0;
create temp.....
....
select finish_time_mins
into finish_time_calculated_in_mins
from temp_result
where ps_race.race_id = in_race_id
order by finish_time_mins asc
limit 1;
return finish_time_calculated_in_mins;
end;

Related

MYSQL - table not updating from Procedure

I want to get distance between two GeoPoints (using LatLong) for that I wrote GETDISTANCE function from solution provided [MySQL Function to calculate distance between two latitudes and longitudes. If I call function independently it works like charm.
As per my understanding I cannot return ResultSet from Function in MySQL so I created Procedure and called function inside procedure As follows:
DELIMITER $$
CREATE PROCEDURE GetNearByGeoPoints(IN Lat REAL, IN Longi REAL)
BEGIN
DECLARE v_max int;
DECLARE v_counter int unsigned default 0;
SET #v_max = (SELECT COUNT(*) FROM TransmitterPointsData);
START TRANSACTION;
WHILE v_counter < v_max
DO
SELECT #coverageID :=CoverageID, #tableLatitude := Latitude, #tableLongitude :=Longitude FROM TransmitterPointsData LIMIT v_counter,1;
SET #Dist= GETDISTANCE(Lat, Longi, tableLatitude, tableLongitude);
UPDATE TransmitterPointsData SET DynamicDistance = #Dist WHERE CoverageID= #coverageID;
set v_counter=v_counter+1;
END WHILE;
COMMIT;
SELECT * FROM TransmitterPointsData;
END $$
DELIMITER ;
What I am trying to do is taking a set of LatLong parameters from user and comparing it with each set of LatLong from table. And after getting output from function I am updating TransmitterPointsData table with where condition on coverageID.
This is my first MySQL query so far I was following syntax but I do not know why I am getting all null values in DynammicDistance Column.
Thank You in Advance
Try replacing the while loop with this:
UPDATE TransmitterPointsData
SET DynamicDistance = GETDISTANCE(Lat, Longi, Latitude, Longitude)
Much shorter, and you avoid potential issues with row selection via limit + offset (which is poor style at best, and gives you a random row each time at worse).

User defined function only returns NULL

I have this following MySQL code:
DELIMITER $$
CREATE FUNCTION durationInMinutes(id INT)
RETURNS INT DETERMINISTIC
BEGIN
DECLARE Minutes INT;
SET Minutes =
(SELECT TIME_TO_SEC(TIMEDIFF(timeDeparture, timeArrival)) FROM AirRoute
WHERE pk_id = id) / 60;
RETURN Minutes;
END$$
DELIMITER;
Basically, this function calculates the duration of a flight in minutes. The parameter is the id of the flight. For some reason though, this function always returns NULL. I even checked this:
SELECT (SELECT TIME_TO_SEC(TIMEDIFF(timeDeparture, timeArrival)) FROM AirRoute
WHERE pk_id = 925) / 60;
This does return the correct answer if I put id = 925, so there could be something wrong with the RETURN statement.
I suspect there is a column called id in the table. I always name parameters and local variables in a way to distinguish them from column names:
CREATE FUNCTION durationInMinutes (
in_id INT
)
RETURNS INT DETERMINISTIC
BEGIN
DECLARE out_Minutes INT;
SELECT out_Minutes := TIME_TO_SEC(TIMEDIFF(timeDeparture, timeArrival))
FROM AirRoute ar
WHERE ar.pk_id = in_id) / 60;
RETURN out_Minutes;
END$$
Ok, I solved it. This is my corrected code:
DELIMITER $$
CREATE FUNCTION durationInMinutes(id INT)
RETURNS INT DETERMINISTIC
BEGIN
RETURN (SELECT TIME_TO_SEC(TIMEDIFF(timeDeparture, timeArrival))
FROM AirRoute
WHERE pk_id = id / 60);
END$$
DELIMITER ;
Still, I really don't understand why it wasn't possible using a temp variable.

GROUP_CONCAT as input of MySQL function

Is it possible to use a GROUP_CONCAT in a SELECT as the input of a MySQL function? I cannot figure out how to cast the variable it seems. I've tried blob. I've tried text (then using another function to break it up into a result set, here) but I haven't had any success.
I want to use it like this:
SELECT
newCustomerCount(GROUP_CONCAT(DISTINCT items.invoicenumber)) AS new_customers
FROM items;
Here is the function:
DROP FUNCTION IF EXISTS newCustomerCount;
DELIMITER $$
CREATE FUNCTION newCustomerCount(invoicenumbers BLOB)
RETURNS INT
DETERMINISTIC
BEGIN
DECLARE new_customers INT;
SET new_customers = 0;
SELECT
SUM(nc.record) INTO new_customers
FROM (
SELECT
1 AS customer,
(SELECT COUNT(*) FROM person_to_invoice ps2 WHERE person_id = ps1.person_id AND invoice < ps1.invoice) AS previous_invoices
FROM person_to_invoice ps1
WHERE invoice IN(invoicenumbers)
HAVING previous_invoices = 0
) nc;
RETURN new_customers;
END$$
DELIMITER ;
Because Mysql functions do not support dynamic queries, I recommend you re-think your basic strategy to pass in a list of invoice numbers to your function. Instead, you could modify your function to accept a single invoice number and return the number of new customers just for the one invoice number.
Also, there are some optimizations you can make in your query for finding the number of new customers.
DROP FUNCTION IF EXISTS newCustomerCount;
DELIMITER $$
CREATE FUNCTION newCustomerCount(p_invoice INT)
RETURNS INT
DETERMINISTIC
BEGIN
DECLARE new_customers INT;
SET new_customers = 0;
SELECT
COUNT(DISTINCT ps1.person_id) INTO new_customers
FROM
person_to_invoice ps1
WHERE
ps1.invoice = p_invoice
AND NOT EXISTS (
SELECT 1
FROM person_to_invoice ps2
WHERE ps1.person_id = ps2.person_id
AND ps2.invoice < ps1.invoice
);
RETURN new_customers;
END$$
DELIMITER ;
Then you can still get the total number of new customers for a given list of invoice numbers like this:
SELECT
SUM(newCustomerCount(invoice)) as total_new_customers
FROM items
WHERE ...
You could try FIND_IN_SET() instead of IN(). The performance will probably be horrible when passing in a long list of invoice numbers. But it should work.
WHERE FIND_IN_SET(invoice, invoicenumbers)
You are looking in the wrong place.
WHERE invoice IN(invoicenumbers) will not do the desired substitution. Instead you need to use CONCAT to construct the SQL, then prepare and execute it.

How to set a variable as query result in a MySQL stored procudure?

A rather simple question, but I can only find answers to more complex questions.
I'm working on a stored procudure and am currently inside a REPEAT loop. I need to run the following query in the loop to get the 1 column value that is returned (only 1 record will be returned). This will need to be stored as a var to be used later in the loop.
SELECT photo_id FROM photos ORDER BY photo_id DESC LIMIT 1;
How do I set that to 'last_photo_id' to be used later in the stored procdure?
You could do something like this:
SELECT #varname := photo_id
FROM photos
ORDER BY photo_id DESC
LIMIT 1;
That is, if you are sure there's no other way to do what you want to do than in a loop. For SQL works best when you use it for set-based solutions.
Try this:
DECLARE total_products INT DEFAULT 0
SELECT COUNT INTO total_products
FROM products
You can use a function
So for example
SET i_camera_count = get_camera_count(i_photo_camera_data_id);
and then make a function like this.
DELIMITER $$
DROP FUNCTION IF EXISTS `get_camera_count` $$
CREATE DEFINER=`root`#`localhost` FUNCTION `get_camera_count`(camera_id_2 INT(10)) RETURNS int(11)
BEGIN
DECLARE v_return_val INT;
DECLARE CONTINUE HANDLER FOR NOT FOUND SET v_return_val = -1;
SELECT x FROM y WHERE camera_id = camera_id_2
RETURN v_return_val;
END $$
DELIMITER ;

Mysql procedure to modify limit and offset

Just a thought in my mind but would it not be possible to createa function in MySQL that can modify the LIMIT within a query
The clause would be a simple calculation function that would work out the offset depending on the pageno * perpage
Example
SELECT * FROM items PAGE(4,20)
this would be the same as
SELECT * FROM items LIMIT 100,20
I have never created any procedures before so the below would be wrong..
CREATE PROCEDURE (int pagno, int limit)
BEGIN
ofsset = roundup(pageno * limit)
END
But within that query actually set the limit and offset for the query?
you can do something like this ofc:
drop procedure if exists foo;
delimiter #
create procedure foo
(
in p_limit int unsigned
)
proc_main:begin
set SQL_SELECT_LIMIT = p_limit;
select * from <table>...
set SQL_SELECT_LIMIT = DEFAULT;
end proc_main #
delimiter ;
call foo(64);