Invalid use of group function when using count() MySQL - mysql

I'm trying to get the average amount of orders per customer and I've tried a bunch of things but I can't find an answer that works to this particular problem.
With pretty much everything I've tried I get Error 1111: Invalid use of group function.
Results on google are only talking about not using where statements so I'm a little lost.
DELIMITER //
create function avgAmount()
returns double
BEGIN
DECLARE AvgAmountOfOrders double;
SET AvgAmountOfOrders = (COUNT(DISTINCT orderID) * 1.0) / NULLIF(COUNT(DISTINCT CustomerID), 0) ;
return AvgAmountOfOrders;
END;//
DELIMITER ;
Select avgAmount();

Thank you The Impaler.
For anyone interested this was the solution.
DELIMITER //
create function test123()
returns double
BEGIN
DECLARE AvgAmountOfOrders double;
SET AvgAmountOfOrders = (select COUNT(DISTINCT OrderID * 1.0) / NULLIF(COUNT(DISTINCT CustomerID), 0)from orders) ;
return AvgAmountOfOrders;
END;//
DELIMITER ;
Select test123();

Related

Slow nested function

Please let me know what information you seek to improve the question, rather than just downvoting.
I have a function that looks like this:
DELIMITER $$
DROP FUNCTION IF EXISTS f_splitadjprice;
CREATE FUNCTION f_splitadjprice (id CHAR(8), startdate DATE)
RETURNS FLOAT
BEGIN
DECLARE splitfactor FLOAT;
DECLARE splitadjprice FLOAT;
SELECT f_splitfactor(id, startdate) INTO splitfactor;
SELECT (f.p_price FROM fp_v2_fp_basic_prices as f WHERE f.fsym_id = id AND
f.p_date = startdate) * splitfactor INTO splitadjprice;
RETURN splitadjprice;
END$$
DELIMITER ;
The function for splitfactor is:
DELIMITER $$
DROP FUNCTION IF EXISTS f_splitfactor;
CREATE FUNCTION f_splitfactor (id CHAR(8), startdate DATE)
RETURNS FLOAT
BEGIN
DECLARE splitfactor FLOAT;
SELECT IFNULL(EXP(SUM(LOG(f.p_split_factor))),1) INTO splitfactor
FROM fp_v2_fp_basic_splits AS f
WHERE f.fsym_id = id AND f.p_split_date > startdate AND f.p_split_date <
NOW();
RETURN splitfactor;
END$$
DELIMiTER ;
The function f_splitadjprice runs extremely slow. About 14 seconds PR row. I have tried to run the individual pieces of the function by themselves. That is, the function call f_splitfactor and SELECT (f.p_price FROM fp_v2_fp_basic_prices as f WHERE f.fsym_id = id AND
f.p_date = startdate). When running these two by themselves outside of the function they take 0,001 seconds to run. So the whole problem is that as soon as I want to do in combination through the nested function it takes 100.000 times longer?
The solution is to not call tables within functions. In general that seems to be bad practice and it is nevertheless extremely slow. One should instead try to get rid of the function and perform the function directly in the query.

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).

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.

Error creating MYSQL function to work out time diff

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;

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 ;