SQL: Sum returns null - mysql

So i have two tables academy_attempt & module_attempt
I am attempting to add two values from each of these tables together:
round(((select
sum(`academy_attempt`.`score`)
from
`academy_attempt`
where
((`academy_attempt`.`module_type_id` in (3 , 4, 5, 6))
and (`academy_attempt`.`user_id` = `U`.`id`))) + (select
sum(ifnull(`module_attempt`.`score`, 0))
from
`module_attempt`
where
((`module_attempt`.`module_type_id` in (3 , 4, 5, 6))
and (`module_attempt`.`user_id` = `U`.`id`)))),
2) AS `total_score`
in academy_attempt the where statement is met and in one row it returns the right amount (if it is alone) however module_attempt does not have any values that matches the where statement and therefor returns null.
Sadly this does not turn into 0 and since im guessing you can't do the operation: 17 + null = 17 it instead returns null.
To counter this i have attempt an IFNULL statement as you can see above but sadly this did not fix the problem

You have to apply the IFNULL() higher up, because an empty result set is considered to be null:
SELECT (...
) + IFNULL((SELECT SUM(`module_attempt`.`score`) ...), 0) AS total_score

NULL represents an unknown value, so naturally trying to add an unknown value to a number still results in an unknown value, albeit a different unknown value (hence NULL != NULL)
I think you actually want the COALESCE function, which returns the first non-null argument. Thus, you can wrap your null value with this function, and sum it as normal. COALESCE( NULL, 0 ) will return 0, and COALESCE(1,0) will return 1
https://dev.mysql.com/doc/refman/5.0/en/comparison-operators.html#function_coalesce

Related

How use NULL value SSRS parameter to retrieve rows containing NULLS

I've read many posts concerning NULLS and SSRS report parameters, but none of them appear to solve my problem.
I have a user-selectable SSRS report parameter #NoteType which can be 1, 3, 4 or NULL. The database table I'm working with (eventText) has a column NoteValue containing 1 of those 4 values. Unfortunately, I can't alter the table design to substitute an integer for that NULL value.
My report works fine if #NoteType = 1, 3 or 4; but not if #NoteType = NULL. If #NoteType = NULL then, depending on my paramaterized WHERE clause (discussed below) my report has either no rows, or it has all of the rows for which NoteValue = 1, 3 or 4. I can't figure out how to retrieve the rows for which NoteValue = NULL if the user selects #NoteType = NULL.
I've configured the #NoteType parameter to allow NULL values, with Note Value as the parameter's value field and Note Type as the label field. The parameter values are obtained from a UNION query:
SELECT ett.NoteValue AS [Note Value],
ett.NoteDescription AS [Note Type]
FROM eventTextType ett
UNION
SELECT NULL AS [Note Value],
'Blank' AS [Note Type]
ORDER BY [Note Type]
The eventTextType table has only 3 rows in which the NoteValue column value is 1, 3 or 4 respectively. There is no row with NoteValue = NULL in the eventTextType table, but there are many such rows in the eventText table. That's why I'm using a UNION query to include NULL as one of the #NoteType parameter values.
The UNION query's output is:
Note Value Note Type
4 Annuity
1 Balance
NULL Blank
3 Division
I suspect that my problem is in the WHERE clause which uses the #NoteType parameter to retrieve rows for the report. Here's what I have tried:
WHERE et.NoteValue = #NoteType -- works fine for #NoteType = 1, 3 or 4; but no rows are returned if #NoteType = NULL
WHERE et.NoteValue = IIF(#NoteType IS NULL, NULL, #NoteType) -- same result
WHERE et.NoteValue = CASE WHEN #NoteType IS NULL THEN NULL ELSE #NoteType END -- same result (expected, since this is equivalent to the IFF version)
WHERE et.NoteValue = COALESCE(#NoteType, et.NoteValue) -- works fine for #NoteType = 1, 3 or 4; but if #NoteType = NULL returns all rows with NoteValue column value 1, 3 or 4 but no rows with NoteValue column value = NULL
What WHERE clause should I be using?
Assuming that your dataset for your main dataset is a dataset query (not a stored proc) then I would simply add a valid parameter value (I've used 0 in this exmaple) to the parameter list and substitute that in the dataset query.
So, change the dataset query that provide parameter values to
SELECT ett.NoteValue AS [Note Value],
ett.NoteDescription AS [Note Type]
FROM eventTextType ett
UNION
SELECT 0 AS [Note Value],
'Blank' AS [Note Type]
ORDER BY [Note Type]
Now in you main dataset query you can change your where clause to this (which will also handle multi-value parameters if that's required)
WHERE ISNULL(et.NoteValue, 0) IN(#NoteType)
Unless I've missed something obvious, that should be it.

Simplify and correct following query

How do I fix, and possibly simplify I guess, this SQL query, please?
SELECT * FROM table WHERE
M IN (NULL,1) AND T IN (NULL,1) AND J IN (NULL,1) AND B IN (NULL,1)
AND (ISNULL(M,0)+ISNULL(T,0)+ISNULL(J,0)+ISNULL(B,0))<4
ORDER BY (ISNULL(M,0)+ISNULL(T,0)+ISNULL(J,0)+ISNULL(B,0)) DESC
table contains 4 columns (M,T,J,B) with 3 possibles values only NULL, 0, 1.
First-line filters to get only entries containing NULL or 1.
Second-line makes an addition to get the total of M+J+T+B and filters only if <4.
Third-line ORDER BY the same Total, M+J+T+B.
error #1582 - Incorrect parameter count in the call to native function 'ISNULL'
MySQL equivalent to ISNULL is IFNULL... answered here
select * from (
select *, coalesce(M,0) M2, coalesce(T,0) T2, coalesce(J,0) J2, coalesce(B,0) B2
from table
where
(M is null or M=1) and
(T is null or T=1) and
(J is null or J=1) and
(B is null or B=1)
)x
where M2+T2+J2+B2 < 4
order by M2+T2+J2+B2 desc
Nulls act strangely in SQL. Any comparison test with a null will result in FALSE, so if you use the phrase Null=Null in a where clause, nothing will be retrieved because the result is always false. I would rewrite your second line to use IsNull functions instead,
IsNull(M,1)*IsNull(T,1)*IsNull(J,1)*IsNull(B,1)>0
Select
t.*,
coalesce(M,0) + coalesce(T,0) + coalesce(J,0) + coalesce(B,0) as calc
from table t
where
coalesce(M,0) in (0,1) and
coalesce(T,0) in (0,1) and
coalesce(J,0) in (0,1) and
coalesce(B,0) in (0,1) and
calc < 4
order by calc desc

having trouble understanding CASE WHEN statement in SQL

SELECT state,
COUNT(CASE WHEN elevation >= 2000 THEN 1 ELSE NULL END) as count_high_elevation_aiports
FROM airports
GROUP BY state;
In the above statement, what is the THEN 1 and what does '1' signify?
How does that value '1' after THEN affect the output?
First, note that these three expressions are equivent:
CASE WHEN elevation >= 2000 THEN 1 ELSE NULL END
IF(elevation >= 2000, 1, NULL)
((elevation >= 2000) OR NULL)
If elevation >= 2000, the expression evaluates as "1", otherwise the expression evaluates as NULL.
"1" is conventionally used as boolean true, and you could substitute the MySQL literal TRUE in the above expressions with equivalent results... but that isn't what the "1" is for, here.
When used with COUNT(), in cases like this, the only real significance of 1 is that it is not NULL.
This is important, because -- contrary to popular belief -- COUNT() does not count rows. It counts values.
What's the difference? NULL is not technically a value. Instead, it is a marker that signifies the absence of a value, thus COUNT(expr) only counts rows where expr is not null.
By using an expression like the one here, you're asking the server to count the rows with elevation => 2000, and you do this by giving COUNT() a NULL for rows you want not to be counted... and a non-null value for rows you do.
Aggregate (GROUP BY) functions operate on values -- and NULL, again, is not a value in this sense.
Another aggregate function that makes this rationale perhaps even more clear is AVG(). If you had 3 rows... with values 5, NULL, and 10... what's the average? If you said 7.5, that's correct: the average of these 3 rows is (5 + 10) ÷ 2 = 5 because the 3 rows have only two values. NULL is not 0, otherwise the average would be (5 + 0 + 10) ÷ 3 = 5, which it is not.
So, that's how and why this works.
How does that value '1' after THEN affect the output?
It really doesn't. You could just as easily have said COUNT(CASE WHEN elevation >= 2000 THEN 'cat videos are funny' ELSE NULL END) because, just like the literal 1, the literal string 'cat videos are funny' is also not null, and non-null values -- anything not null -- is what count will count.
A novice might try to accomplish this task with COUNT(elevation >= 2000), but that gives the wrong answer, because the 0 (false) for rows where elevation is < 2000 is not null, so these rows would still be counted.
You may then ask, "why not just use COUNT(*) ... WHERE elevation >= 2000?" Good question. The reasons vary, but if you GROUP BY state and there are states with no rows matching WHERE, those states would be entirely eliminated from the results, which is often not what you want. This query includes them, with a count of zero.
Note that ((elevation >= 2000) OR NULL), the third example expression at the top, doesn't actually need the parentheses. I included them because this form is not necessarilly intuitive at first glance. The natural precedence of operations will cause this to be evaluated correctly if written simply elevation >= 2000 OR NULL. This expression is equivalent to the other two because elevation >= 2000 first evaluates to 1 if true, 0 if false, or NULL if elevation is null. Then the lower-precedence OR is evaluated, and you get one of these: 1 OR NULL => 1 ... 0 OR NULL => NULL ... NULL OR NULL => NULL... and you may actually be awarded a SQL wizard badge by the elders of the Internet at the point when writing queries with COUNT(elevation >= 2000 OR NULL) comes naturally to you.
Query will simply return 1 if elevation is > or = 2000, else it will return NULL (this is use full for boolean representation of field because NULL represents 0),
Now returned value will be set into count_high_elevation_airports.

SUM of each row is counting null values as 0. How do I make mysql skip null values?

Well, the values aren't null per say, they are in VARCHAR(30) but in decimal form. However some of the records have "NA" in some fields. I would like mysql to skip those rows in SUM calculation when "NA" is present in the fields used for the SUM. Mysql is treating all incalculable fields as 0. The 0 from fields containing "NA" is misleading. I am doing a GROUP BY TABLE.ID.
Edit:
SELECT
SUM(
CASE
WHEN X >1 THEN 1
WHEN X<-1 THEN 2
ELSE 3
END
CASE
WHEN Y >1 THEN 1
WHEN Y <-1 THEN 2
ELSE 3
END)
AS "Col X+Y";
FROM TableA
GROUP BY TableA_ID;
Sometimes X and/or Y = "NA" on certain fields. I get 6 if both X and Y on TableA_ID = 17 or other numbers when one of them is "NA".
Edit (quoting my comment on VARCHAR):
"I tried storing my values as DEC(5,2), but some of the data from Excel have NA's in the fields. I did set X DEC(5,2) NULL and tried inserting NA into it but kept getting an error (cannot be null). I also tried making the default value "NA" but still get an error (cannot be null). I'll add in a sample query as edit."
I got it. I added in another WHERE clause using
WHERE ..... AND(Colx IS NOT NULL OR Coly IS NOT NULL OR ......);
I switched back the values to DEC(3,1) and made the fields NULLable with defaults null if the field value is NULL. I had to understand how to use NULL. I took out the 'NA's in Excel and left those field values blank.
if foo1 is null or NA just sum it as zero (the neutrum value in the addition), otherwise sum the value.
select sum( case when foo1 is null or foo1 = 'NA' then 0 else foo1 end) as sum, foo2
from FooTable group by foo2
or
select sum(foo1) from FooTable
where (foo2 <> 'NA' and foo2 is null) and (foo3 <> 'NA' or foo3 is null )
group by foo4

TSQL Filter in sproc failing on numeric conversion

I have a sub-query that returns a value. If that value is greater than a range selected by the outside query, it should return the highest value of the outside query.
Given the following:
DECLARE #ExternalPixelValue VARCHAR(MAX)
DECLARE #SecondsTriggerValue INT
DECLARE #Trigger INT
SELECT TriggerValue
FROM vw_ContainerBehaviouralPixels
WHERE TriggerValue < 9000
SELECT
#ExternalPixelValue = ExternalPixelValue,
#SecondsTriggerValue = CONVERT(INT, TriggerValue),
#Trigger = SUM(p.LastEventTimeSpan)
FROM
PageVisitEvents p
RIGHT JOIN
vw_ContainerBehaviouralPixels vwc ON p.AccountContainerID = vwc.AccountContainerID
WHERE
vwc.SystemBehaviouralSegmentID = 2 -- minimum time on site
AND
CONVERT(INT, vwc.TriggerValue) < (SELECT 99999)
GROUP BY
ExternalPixelValue, TriggerValue
SELECT #ExternalPixelValue, #SecondsTriggerValue, #Trigger
The first query returns values: 10, 30, 60, 90, 180, 300, 360.
Now, putting in a value in the subquery of 11 returns 10; anything between 31 and 60 returns 30, and so forth. Problem is that when I enter anything in 180, 90 is always returned. When I change the table value from 90 to 99, still, only 99 is returned.
TriggerValue is VARCHAR(512) which is why the conversion up there. If figured the filter is recognizing the character, and not the numeric value. However after using CONVERT and CAST, I'm still seeing the same issue.
Any advice?
Thanks.
You are populating the variables
#ExternalPixelValue, #SecondsTriggerValue, #Trigger
over and over, so in the end, you have no control of the values they contain (the value from the last read)
If you write a syntax like that, make sure you only get 1 result for each variable or that you don't care which result is chosen.