Aggregate queries remove zeros - mysql

I need to calculate the averages of a couple of columns which range from 0 to 5. However I want to exclude the zero's from the AVG calculation.
So I have something like
SELECT AVG(column1), AVG(column2), AVG(column3) FROM table1
WHERE ???

AVG() only counts records that are non-null.
NULLIF() will return null, when the value matches the 2nd parameter.
SELECT AVG(NULLIF(column1,0)), AVG(NULLIF(column2,0)), AVG(NULLIF(column3,0)) FROM table
WHERE ????
You could create a more complex AVG() by using IF(column1 IN (x,y,z), column1, NULL) within.
NOTE: If your column's data is a REAL or FLOAT, then you should ROUND() or TRUNCATE() your value within the NULLIF(). EX:
AVG( NULLIF( ROUND(column1,3), 0.000 )) AS column1
-- JJ --

You can do something like this, where you sum the colummn and divide by the count of rows where the value is not equal to zero:
SELECT SUM(Column1) / SUM (IF(Column1 = 0, 0, 1)),
SUM(Column2) / SUM (IF(Column2 = 0, 0, 1)),
SUM(Column3) / SUM (IF(Column3 = 0, 0, 1))
FROM table1
Not you will also have to trap divide by zero in the denominator (which i haven't done above)...

Since the WHERE clause excludes or includes entire rows, you'll need three different queries. You can combine them into a single query though, thanks to sub-queries:
SELECT (SELECT AVG(column1) FROM table WHERE column1 != 0) as avg1,
(SELECT AVG(column2) FROM table WHERE column2 != 0) as avg2,
(SELECT AVG(column3) FROM table WHERE column3 != 0) as avg3;

You can calculate your own averages by summing the values you care about and dividing by the count of values you care about, like so:
select
sum(case when column1 = 0 then 0 else column1 end) /
sum(case when column1 = 0 then 0 else 1 end) as avg1,
sum(case when column2 = 0 then 0 else column2 end) /
sum(case when column2 = 0 then 0 else 1 end) as avg2,
sum(case when column3 = 0 then 0 else column3 end) /
sum(case when column3 = 0 then 0 else 1 end) as avg3
FROM table1

Related

MySQL : using sum in( case when ) statement shows 0 as result

new to MySQL..so pls help me out with this basic code..
i have a query something like this...
select weekofyear(id_time),
(id),
#Tat1:=exp1,
#Tat2:=exp2,
#check1:=exp3,
#check2:=exp4,
(case when #check2=0 then
(case when (#Tat1>(#Tat2+30) or (#check1=1 and (#Tat1>#Tat2+10))) then 1 else 0 end)
else
(case when (#Tat1>(#Tat2+30) or (#check1=1 and (#Tat1>#Tat2+20))) then 1 else 0 end)
end) as BO
from datb
where cid=18
and id_time between '2019-11-01 06:00:00' and '2019-11-25 06:00:00'
and it gives correct results as--here
however i want to use sum after case when statement so that I can get total values where BO=1 and group by week of year , so i made following changes-
select weekofyear(id_time),
count(id),
#Tat1:=exp1,
#Tat2:=exp2,
#check1:=exp3,
#check2:=exp4,
sum(case when #check2=0 then
(case when (#Tat1>(#Tat2+30) or (#check1=1 and (#Tat1>#Tat2+10))) then 1 else 0 end)
else
(case when (#Tat1>(#Tat2+30) or (#check1=1 and (#Tat1>#Tat2+20))) then 1 else 0 end)
end) as BO
from datb
where cid=18
and id_time between '2019-11-01 06:00:00' and '2019-11-25 06:00:00'
group by weekofyear(id_time)
but it always returns 0 as output.
Output --here 2
Please help , I don't know what am I doing wrong here.
Thanx !
As others have already said, session variables can be unpredictable (especially when aggregation gets mixed in). That said, it doesn't look like you're using the session variables to carry over values from one row to the next (as is often done), but to just make aliases of sorts for calculations you don't want to repeat.
A better way to handle that is just through subqueries.
SELECT woy, id, Tat1, Tat2, check1, check2
, CASE
WHEN check2=0 THEN (
CASE
WHEN (Tat1>(Tat2+30) OR (check1=1 AND (Tat1>Tat2+10))) THEN 1
ELSE 0
END
)
ELSE (
CASE WHEN (Tat1>(Tat2+30) OR (check1=1 AND (Tat1>Tat2+20))) THEN 1
ELSE 0
END
)
END AS BO
FROM (
SELECT WEEKOFYEAR(id_time) AS woy
, id
, exp1 AS Tat1
, exp2 AS Tat2
, exp3 AS check1
, exp4 AS check2
FROM datb
WHERE cid=18
AND id_time BETWEEN '2019-11-01 06:00:00' AND '2019-11-25 06:00:00'
) AS subQ
;
You can then tweak the above query for aggregation, or use it as a subquery for an aggregating outer query.

How to change the value of sum function inside the query?

I have a query with sum aggregation function :
SELECT sum(case when result=1 then 1 when result=2 then 0)as final_result From results
I want to change this part when result=2 then 0 to something like that when result=2 then final_result equals zero
Is it possible to do this ? or there is another way for that?
Try this :
SELECT CASE WHEN cnt=sum THEN sum ELSE 0 END as Final_result
FROM
(
SELECT count(*) as cnt,
SUM(case when result=1 then 1 else 0 end) as sum
from results
) Temp
Working Fiddle here.
Explanation:
Inner query select the total number of records and the sum of the records. Then if those count and sum are equal that means all values are 1.
NB: Removed the checking when result=2 then 0 because, the query will select 0 for any values other than 1. So there is no need to check for result=2.
EDIT:
To find the count of 10 consecutive 1's, you can do:
SELECT SUM(CASE WHEN RN=10 THEN 1 ELSE 0 END) AS final_result
FROM
(
SELECT CASE WHEN result=1 THEN #row_number:=#row_number+1 ELSE #row_number:=0 END AS RN,
result
FROM results, (SELECT #row_number:=0) AS t
) Temp
Sample Fiddle here.
I think you want something like this:
SELECT case max(case result when 2 then 1 else 0 end)
when 1 then 0
else sum(case when result=1 then 1 else 0 end)
end as final_result
From results
Fiddle here
So what I'm doing is checking for the final-result-must-be-0 condition inside a MAX(). That way, if any of the rows meets this condition (i.e. result = 2), the maximum value will be 1. Then I can use that value to determine whether I wanna return 0 or do the SUM().

Calculating an average number from three columns

I am trying to calculate an average number from three columns but only include the column in the calculation if column is not null and is bigger than 0;
for example the average usually is
(column1 + column2 + column3) / 3
but if column3 is null or 0 then it will be
(column1 + column2 + column3) / 2 or (column1 + column2 ) / 2
I have this sol far but it is not complete. the average is wrong when one of the columns is 0 (0 is default)
SELECT movie.title,
movie.imdbrating,
movie.metacritic,
tomato.rating,
((imdbrating + metacritic + tomato.rating)/3) as average
FROM movie, tomato
WHERE movie.imdbid = tomato.imdbid
How can I implement this?
I'm fixing the rest of the query to use table aliases and proper join syntax. But the case statements are what you really need:
SELECT m.title, m.imdbrating, m.metacritic,
t.rating,
((case when imdbrating > 0 then imdbrating else 0 end) +
(case when metacritic > 0 then metacritic else 0 end) +
(case when t.rating > 0 then t.rating else 0 end) +
) / nullif(coalesce((imdbrating > 0), 0) + coalesce((metacritic > 0), 0) + coalesce((t.rating > 0), 0)), 0)
FROM movie m JOIN
tomato t
ON m.imdbid = t.imdbid;
The denominator is using a convenient MySQL extension where booleans are treated as 0 or 1 in a numeric context. The nullif() returns NULL if no rating meets the conditions. And, the > 0 is is not true for NULL values.
Try this:
SELECT (IF Column3 IS NULL OR Column3=0, AVG(Column1+Column2), Avg(Column1+Column2+ Column3)) as Result FROM Table
EDIT:
If any of the tree columns can be nullm, try this:
SELECT AVG(IF(Column1 IS NULL,0,Column1)+IF(Column2 IS NULL,0,Column2)+IF(Column3 IS NULL,0,Column3)) as Result FROM Table
UPDATE
(ifnull(column1,0)+ifnull(column2,0)+ifnull(column3,0))/
nullif((abs(sign(ifnull(column1,0)))
+abs(sign(ifnull(column2,0)))
+abs(sign(ifnull(column3,0)))), 0)
Can handle Negative values.
Demo to test

get count of two table fields in one query

I am trying to get the count of females and males in the gender field of a table.
Is there a way to get the count of each in one query?
Something like:
select * from table count(where gender = 'm') as total_males, count(where gender = 'f') as total_females;
or will it require two queries?
select count(*) from table where gender = 'm';
select count(*) from table where gender = 'f';
This is basically a PIVOT. MySQL does not have a pivot so you can use an aggregate function with a CASE statement to perform this:
select
sum(case when gender = 'm' then 1 else 0 end) Total_Male,
sum(case when gender = 'f' then 1 else 0 end) Total_Female
from yourtable
See SQL Fiddle with Demo
Or using COUNT:
select
count(case when gender = 'm' then 1 else null end) Total_Male,
count(case when gender = 'f' then 1 else null end) Total_Female
from yourtable;
See SQL Fiddle with Demo
Something like this will work:
SELECT SUM(IF(t.gender='m',1,0)) AS total_males
, SUM(IF(t.gender='f',1,0)) AS total_females
FROM mytable t
The "trick" here is that we are using a conditional test to return either a 0 or a 1 for each row, and then adding up the 0's and 1's. To make this a little more clear, I am using the SUM aggregate function rather than COUNT, although COUNT could be used just as easily, though we'd need to return a NULL in place of the zero.
SELECT COUNT(IF(t.gender='m',1,NULL)) AS total_males
, COUNT(IF(t.gender='f',1,NULL)) AS total_females
FROM mytable t
Consider that the two expressions in the SELECT list of this query:
SELECT COUNT(1)
, SUM(1)
FROM mytable t
Will return the same value.
If you want to avoid the MySQL IF function, this can also be done using the ANSI SQL CASE expression:
SELECT SUM( CASE WHEN t.gender = 'm' THEN 1 ELSE 0 END )) AS total_males
, SUM( CASE WHEN t.gender = 'f' THEN 1 ELSE 0 END )) AS total_females
FROM mytable t
select sum(case when gender='m' then 1 else null end) as total_males, sum(case when gender='f' then 1 else null end) as total_females from ...
Should work just fine!
If your only issue is to avoid two queries, you can always write two queries as subselects of one query.
Select (select 1 from dual) as one, (select 2 from dual) as two from dual
This would work for your scenario, too.

MySql: is it possible to 'SUM IF' or to 'COUNT IF'?

I have a column 'hour'
I have a column 'kind' (it can be 1,2 or 3)
I'd like to do something like:
SELECT count(id), SUM(hour) as totHour, SUM( IF ( kind = 1, 1, 0 ) ) as countKindOne
or
SELECT count(id), SUM(hour) as totHour, COUNT( IF ( kind = 1 ) ) as countKindOne
But mysql tell me I've an error... what's the error!?
Please see this stackoverflow topic: MySQL SUM IF field b = field a
.. I'm not able to reply this ...
You can use a CASE statement:
SELECT count(id),
SUM(hour) as totHour,
SUM(case when kind = 1 then 1 else 0 end) as countKindOne
you want something like:
SELECT count(id), SUM(hour) as totHour, SUM(kind=1) as countKindOne;
Note that your second example was close, but the IF() function always takes three arguments, so it would have had to be COUNT(IF(kind=1,1,NULL)). I prefer the SUM() syntax shown above because it's concise.
You can also use SUM + IF which is shorter than SUM + CASE:
SELECT
count(id)
, SUM(IF(kind=1, 1, 0)) AS countKindOne
, SUM(CASE WHEN kind=2 THEN 1 ELSE 0 END) AS countKindTwo
There is a slight difference between the top answers, namely SUM(case when kind = 1 then 1 else 0 end) and SUM(kind=1).
When all values in column kind happen to be NULL, the result of SUM(case when kind = 1 then 1 else 0 end) is 0, whereas the result of SUM(kind=1) is NULL.
An example (http://sqlfiddle.com/#!9/b23807/2):
Schema:
CREATE TABLE Table1
(`first_col` int, `second_col` int)
;
INSERT INTO Table1
(`first_col`, `second_col`)
VALUES
(1, NULL),
(1, NULL),
(NULL, NULL)
;
Query results:
SELECT SUM(first_col=1) FROM Table1;
-- Result: 2
SELECT SUM(first_col=2) FROM Table1;
-- Result: 0
SELECT SUM(second_col=1) FROM Table1;
-- Result: NULL
SELECT SUM(CASE WHEN second_col=1 THEN 1 ELSE 0 END) FROM Table1;
-- Result: 0
From MYSQL I solved the problem like this:
SUM(CASE WHEN used = 1 THEN 1 ELSE 0 END) as amount_one,
Hope this helps :D
It is worth noting that you can build upon Gavin Toweys answer by using multiple fields from across your query such as
SUM(table.field = 1 AND table2.field = 2)
You can also use this syntax for COUNT and I am sure other functions as well.