Calculating an average number from three columns - mysql

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

Related

When Current column and Previous column > 0, then do this

mysql> select * from table;
+------+------+------+-------+
| id | cnta | cntb | cntc |
+------+-------------+-------+
4 0 1 2
3 2 3 0
2 1 0 1
1 3 2 2
I would like to compare two sequential rows (current column and previous column) and if they are both greater than 0, I'd like to sum the results of the sequential rows.
this is what I tried and failed:
SELECT
g1.id,
(case
When g2.cnta > 0 and g1.cnta > 0 then g1.cnta ELSE 0) End as cnta +
(case
When g2.cntb > 0 and g1.cntb > 0 then g1.cntb ELSE 0) End as cntb +
(case
When g2.cntc > 0 and g1.cntc > 0 then g1.cntc ELSE 0) End as cntc
FROM table g1 INNER JOIN table g2 ON g2.id = g1.id+ 1;
the final output I'm trying to get is like this (if current column and previous column > 0, then current column1 + etc ) :
id totalcnt
4 1
3 2
2 2
1
How can I fix my query? or can I get alternative approach as a solution, please?
** I forgot to mention that there are no NULL values in my table. Only 0s and positive integers.
If your last row of the result is not to be empty, try this:
SELECT
t1.id,
(
CASE WHEN t1.cnta>0 AND t2.cnta>0 THEN t1.cnta ELSE 0 END
+
CASE WHEN t1.cntb>0 AND t2.cntb>0 THEN t1.cntb ELSE 0 END
+
CASE WHEN t1.cntc>0 AND t2.cntc>0 THEN t1.cntc ELSE 0 END
) AS cValue
FROM
table1 t1
LEFT JOIN table1 t2 ON t2.id=t1.id-1;
OR if you really want it to be empty, you can use a subquery
SELECT
t1.id,
IFNULL(
(
SELECT
(
CASE WHEN t1.cnta>0 AND t2.cnta>0 THEN t1.cnta ELSE 0 END
+
CASE WHEN t1.cntb>0 AND t2.cntb>0 THEN t1.cntb ELSE 0 END
+
CASE WHEN t1.cntc>0 AND t2.cntc>0 THEN t1.cntc ELSE 0 END
)
FROM
table1 t2 WHERE t2.id=t1.id-1)
,'') AS cValue
FROM table1 t1
SELECT t1.id,
(t1.cnta * t2.cnta > 0) * t1.cnta
+ (t1.cntb * t2.cntb > 0) * t1.cntb
+ (t1.cntc * t2.cntc > 0) * t1.cntc totalcnt
FROM test t1
LEFT JOIN test t2 ON t1.id = t2.id + 1;
https://dbfiddle.uk/?rdbms=mysql_5.7&fiddle=48f296035e95bf4c7331427e82c25619
t1.cntX * t2.cntX is NULL if at least one value is NULL, is zero if at least one value is zero, and is 1 if both values are not zero/NULL.

SQL return having sum value

I have a query that checks a group and makes sure that it has more than 1 value under 2
SELECT `tile` FROM TFResults
GROUP BY `tile`
HAVING SUM(CASE WHEN `Place` < 2 THEN 1 ELSE 0 END)> 1 ;
I would like to return the value of sum also but can't seem to get it to work
SELECT `tile`, thesum
FROM TFResults
GROUP BY `tile`
HAVING SUM(CASE WHEN `Place` < 2 THEN 1 ELSE 0 END) as thesum > 1 ;
You define alias names in the select clause
SELECT tile,
SUM(CASE WHEN Place < 2 THEN 1 ELSE 0 END) as thesum
FROM TFResults
GROUP BY tile
HAVING thesum > 1
First you need to move sum part to select statement. And if you need only one column to check then use if instead CASE. Check Below
SELECT tile, SUM(if(Place < 2, 1, 0)) place_sum
FROM TFResults
GROUP BY tile
HAVING place_sum > 1
Just move sum statement to select:
SELECT
`tile`,
SUM(CASE WHEN `Place` < 2 THEN 1 ELSE 0 END) AS thesum
FROM TFResults
GROUP BY `tile`
HAVING thesum > 1 ;

Aggregate queries remove zeros

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

count not null columns out of specific list of columns and then compare in where clause for each row

Table structure:
Table Structure http://imagebin.org/index.php?mode=image&id=238883
I want to fetch data from both the tables which satisfy some of the conditions like
WHERE batch='2009', sex='male',course='B.Tech', branch='cs', xth_percent>60,
x2percent>60, gradpercent>60 and (if ranktype='other' ) then
no._of_not_null_semester>number elseifranktype='Leet') then
no._of_not_null_semester>number-2
sem 1-8 contains percentage for 8 semesters, and I want to filter results for each student if they have cleared 3 semesters or 4 semester i.e. not null values out of 8 values
no._of_not_null_semester
needs to be calculated, it is not a part of database, need help with that as well.
Required Query
SELECT * FROM student_test
INNER JOIN master_test ON student_test.id=master_test.id
WHERE sex='male' and batch='2009' and course='B.Tech'
and xthpercent>60 and x2percent>60 and
WHEN ranktype='Leet' THEN
SUM(CASE WHEN sem1 IS NOT NULL THEN 1 ELSE 0
WHEN sem2 IS NOT NULL THEN 1 ELSE 0
WHEN sem3 IS NOT NULL THEN 1 ELSE 0
WHEN sem4 IS NOT NULL THEN 1 ELSE 0
WHEN sem5 IS NOT NULL THEN 1 ELSE 0) >2
ELSE
SUM(CASE WHEN sem1 IS NOT NULL THEN 1 ELSE 0
WHEN sem2 S NOT NULL THEN 1 ELSE 0
WHEN sem3 IS NOT NULL THEN 1 ELSE 0
WHEN sem4 IS NOT NULL THEN 1 ELSE 0
WHEN sem5 IS NOT NULL THEN 1 ELSE 0) >4
Without changing the structure you can't use COUNT to achieve this.
One way to solve the problem would be to create a semester table which would contain a row for each finished semester for each student. This would have a foreign key pointing to test_student.id and you could use COUNT(semester.id)
If that is an option for you, it would be the best solution.
EDIT:
Check this out, didn't test the query but should work generally
I decided to do the math in the select itself to prevent calculating the same thing twice.
The HAVING conditions are applied after your result is ready to deliver, just before a LIMIT.
In terms of speed optimization you could try and move the sSum block into the WHERE condition just like you had it before. Probably it doesn't make much of a difference
SUM() does not work because it is an aggregate function which summarizes values in a column
I did some changes to your query in addition:
don't SELECT *, select specific fields and add a table identifier. ( in this case I used the aliases s for student_test AND m for master_test )
you put s.batch = '2009' into single quotes - if the field batch is an integer field, you should use s.batch = 2009, which would prevent MySQL from casting every single row to string to be able to compare it (int = int much faster than cast(int as varchar) = varchar) same about the other numeric values in your table
The Query:
SELECT
s.id,
s.sex,
s.course,
s.branch,
(
IF ( m.sem1 IS NOT NULL, 1, 0 ) +
IF ( m.sem2 IS NOT NULL, 1, 0 ) +
IF ( m.sem3 IS NOT NULL, 1, 0 ) +
IF ( m.sem4 IS NOT NULL, 1, 0 ) +
IF ( m.sem5 IS NOT NULL, 1, 0 ) +
IF ( m.sem6 IS NOT NULL, 1, 0 ) +
IF ( m.sem7 IS NOT NULL, 1, 0 ) +
IF ( m.sem8 IS NOT NULL, 1, 0 )
) AS sSum
FROM
student_test s
INNER JOIN master_test m ON m.id = s.id
WHERE
s.sex = 'male'
AND
s.batch = '2009' # I dont see this field in your database diagram!?
AND
s.course = 'B.Tech'
AND
m.xthpercent > 60
AND
m.x2percent > 60
HAVING
(
m.ranktype = 'OTHER'
AND
sSum > 4
)
OR
(
m.ranktype = 'LEET'
AND
sSum > 2
)
If you're generally interested in learning database design and usage I found an very interesting opportunity for you.
Stanford University offers a free database class "Introduction to databases". This is free and will cost you approx. 2 hours a week for 3 weeks, final exam included.
https://class2go.stanford.edu/db/Winter2013/preview/
SELECT
s.id,
s.sex,
s.course,
s.deptt,
m1.id,
m1.xthpercent,
m1.x2percent,
m1.sem1,
m1.sem2,
m1.sem3,
m1.ranktype,
m1.sem4,
m1.sem5,
m1.sem6,
m1.sem7,
m1.sem8,
m1.sSum
FROM
student_test s
INNER JOIN(SELECT m.id,
m.xthpercent,
m.x2percent,
m.sem1,
m.sem2,
m.sem3,
m.ranktype,
m.sem4,
m.sem5,
m.sem6,
m.sem7,
m.sem8,
( IF ( ceil(m.sem1)>0, 1, 0 ) +
IF ( ceil(m.sem2)>0, 1, 0 ) +
IF ( ceil(m.sem3)>0, 1, 0 ) +
IF ( ceil(m.sem4)>0, 1, 0 ) +
IF ( ceil(m.sem5)>0, 1, 0 ) +
IF ( ceil(m.sem6)>0, 1, 0 ) +
IF ( ceil(m.sem7)>0, 1, 0 ) +
IF ( ceil(m.sem8)>0, 1, 0 )
) AS sSum FROM master_test m
WHERE m.xthpercent>60 and
m.x2percent>60
HAVING (m.ranktype='Leet' AND sSum>2 )
OR
(m.ranktype != 'Leet') AND sSum>4 )
as m1 ON m1.id = s.id
WHERE
s.sex='Male'
and
s.course='B.Tech'
and
s.deptt='ELE'
This is the query finally I'm using, Love that query :)

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.