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

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

Related

Check for all null values in a GROUP BY [duplicate]

This question already has answers here:
MySQL AVG() return 0 if NULL
(3 answers)
Closed last month.
I have the following structre
id val
1 ...
.
.
2 ...
.
.
3 null
3 null
3 null
4 ...
.
.
Basically each id has multiple no. of values. And an id has either all values as integers or all values as null
What I want is to perform an aggregate (like AVG) on val group by id. If that id has null values, I want to put 5 there.
#1
SELECT id, (CASE SUM(val) WHEN null THEN 5 ELSE AVG(val) END) AS ac FROM tt GROUP BY id
> executes ELSE even for id = 3
In CASE, there should be an aggregate function that when done on null values give null.
I checked SUM and MAX like
SELECT SUM(val) FROM tt WHERE id = 3
> null
and it gives null here but doesn't work in main statement. I guess it is related to the type of equality and hence tried WHEN IS NULL but its a syntax error.
Also, is there some more standard way of indicating group of values as all null rather than using SUM or MAX.
You can use if condition :
select id, If(sum(val) is null, 5, AVG(val)) as average
FROM tt
group by id
check here : https://dbfiddle.uk/Uso9nNTM
The exact problem with your CASE expression is that to check for null in MySQL we have to use IS NULL rather than equality. So use this version:
CASE WHEN SUM(val) IS NULL THEN 5 ELSE AVG(val) END
But we might as well just use COALESCE() to assign an average of 5 for those id groups having all null values.
SELECT id, COALESCE(AVG(val), 5) AS avg_val
FROM tt
GROUP BY id;
Note that the AVG() function by default ignores nulls. Therefore, the expression AVG(val) would only be null if every record in an id group were having null for val.

Mysql select ignores null values on return results

I have a table where a column is mostly NULL except for one row.
ID STATUS VALUE POS
1 'BAD' 200 0
2 NULL 200 0
3 NULL 300 1
4 'OK' 0 2
if I do
Select * from table where STATUS != 'OK'
I expect
ID STATUS VALUE POS
1 'BAD' 200 0
2 NULL 200 0
3 NULL 300 1
But I get
ID STATUS VALUE POS
1 'BAD' 200 0
I want to know why, I know I can do something else like Where ID = 4, but why the query above returns an empty result for NULL values?
Thanks
Comparing with NULL is always NULL. Think about NULL like it is "unknown value". Does some unknown value is not equal to 'OK'? this is unknown... so the result is NULL.
In logical expressions NULL is treated as FALSE. So you do not receive the rows which you want to receive.
You must either apply additional check for NULL value (WHERE status != 'OK' OR status IS NULL) or convert NULL value to some definite constant value before compare (WHERE COALESCE(status, '') != 'OK').
Pay attention - this interpretation differs from one used in CHECK constraint (including FOREIGN KEY constraint) in which NULL value is treated as "matched".
It isn't possible to use "equal" or "not equal" for NULL values
You MUST use IS NULL or IS NOT NULL e.g:
Select * from table where STATUS != 'OK' OR Status IS NULL

CASE with IS NULL not replacing as expected

I have a simple column with some numbers, which looks like;
133
8
55
11
NULL
NULL
235
NULL
I want to put a default in when selecting this column, with the aim of replacing the nulls. I've done;
CASE
WHEN round(avg(s.seconds)) IS NULL THEN 0
ELSE round(avg(s.seconds))
END as 'seconds'
However, my nulls remain. My expectation is that they would be set to 0. Why would this not work?
NULL values are ignored in average calculations.
If you want to replace NULL values for 0 in your average, you need to use COALESCE on the values, inside the average function. Example follows:
DROP TEMPORARY TABLE IF EXISTS TEMP1;
CREATE TEMPORARY TABLE TEMP1
SELECT 1 AS price
UNION ALL SELECT NULL AS price
UNION ALL SELECT NULL AS price
UNION ALL SELECT 3 AS price;
SELECT * FROM TEMP1; # List values (1, NULL, NULL, 3)
SELECT AVG(price) FROM TEMP1; # Returns 2 (NULL values IGNORED)
SELECT AVG(COALESCE(price,0)) FROM TEMP1; # Returns 1 (NULL values REPLACED with 0)
You can do it by 3 ways
1
round(avg(CASE
WHEN s.seconds IS NULL THEN 0
ELSE s.seconds
END)) as 'seconds'
2
The MySQL IFNULL() function lets you return an alternative value if an
expression is NULL:
round(avg(IFNULL(s.seconds,0)))
3
or we can use the COALESCE() function, like this:
round(avg(COALESCE(s.seconds,0)))

SQL case giving inconsistent result

I am getting an inconsistent result from below two SQL queries.
Query1 : select (case when 'Abc' = null then 1 else 0 end) from dual
Query2 : select (case when ('Abc' <> null) then 1 else 0 end) from dual
Result for both queries same i.e
0
What's wrong am I missing some thing ?
NOTE : : I know I can use IS NULL and IS NOT NULL but my question is why result from above queries is inconsistent.
EDIT : Added from answer of #ppeterka.
select (case when null = null then 1 else 0 end) from dual
This returns 0 too. Null is not even equal to itself.
But then what does this return?
select (case when null <> null then 1 else 0 end) from dual
0 again
SQLFiddle link
Because NULL is unknown that is why the result is 0. When you want to compare a column or a value if it is null or not, use IS NULL or IS NOT NULL.
select (case when 'Abc' IS null then 1 else 0 end) from dual -- 0
select (case when ('Abc' IS NOT null) then 1 else 0 end) from dual -- 1
SQLFiddle Demo
The result of comparing anything to NULL, even itself, is always NULL(not TRUE or FALSE).
Searched CASE expression:
If no Boolean_expression evaluates to TRUE, the Database Engine returns the else_result_expression if an ELSE clause is specified, or a NULL value if no ELSE clause is specified.
In your case the result always will be 0, because 0 in ELSE clause
To add a bit of twist to this, try this out:
select (case when null = null then 1 else 0 end) from dual
This returns 0 too. Null is not even equal to itself.
But then what does this return?
select (case when null <> null then 1 else 0 end) from dual
This returns 0 again! Oh holy... It is not even not equal to itself, while it is not equal to itself... Quite a situation to grasp without getting insane...
Why to keep this all in mind? - one might ask
One example is indexing: in Oracle, indexes don't work the way one would expect them to work on NULL values where a column permits the use of that value. This means that given an index, if all the values (fields, functions on fields, etc) of a row that are included in the index are all NULL, that row won't be indexed in that given index. So this means that an index, that has only one value indexed (for example, a field directly), a null value would mean that row not to be included in the index.
To overcome this
it might be advisable to add an index with a distinct, exact value semantically representing the NULL meaning, like NVL(mynullcol,-1) on a column only containing positive integers to be able to query them quickly.
or you could add a constant value to form a "semi-multivalue" index, that indexes all rows, where one can be null, as the other is a constant. (create index idx_myindex on table(column_with_nulls,1); )
(This question and this article detail this subject in a bit more depth)
Another example is ordering...
select (case when null < null then 1 else 0 end) from dual;
select (case when null > null then 1 else 0 end) from dual;
Both 0. This is OK... We expected this by now... And what about this?
select (case when 'Abc' > null then 1 else 0 end) from dual;
select (case when null > 'Abc' then 1 else 0 end) from dual;
Uh-oh... Both 0 again. This might be an issue - how is ordering going to work?
select col_1 from
(select null as col_1 from dual)
union all (select 'Abc' as col_1 from dual)
union all (select null as col_1 from dual)
union all (select null as col_1 from dual)
order by col_1
This however consistently returns:
Abc
null
null
null
Using ... order by col_1 DESC returns:
null
null
null
Abc
So from this, on an empirical basis, it does seem that 'Abc' < null... However, as per the valuable comment of #ypercube:
the sort order can be set with the NULLS LAST and NULLS FIRST modifiers (at least in Oracle).
What you observe is the default sort order when the ORDER BY has no modifier
NULL is a twisted business, it is wise to steer away from it if it is possible... (And this is not only true for SQL, but for certain situations in OOP languages too.)
You can't use <> or = with nulls. You need to say
select (case when 'Abc' is null then 1 else 0 end) from dual
and
select (case when 'Abc' is not null then 1 else 0 ) from dual
It is not inconsistent.
if you have 2 objects A,B then it's one of the following three:
A equals B
A not equals B
you cannot compare A and B
It's like examining if (0/0 > 0) or (0/0 < 0) or (0/0 = 0). You just cannot compare them. every option is false
For your example: case checks if your argument is true
argument ('abc'=null) is not true, it's null
argument ('abc'<>null) is not true, it's null
The value NULL means the data value for the column is Unknown. A NULL is not synonymous with Zero, or zero length string or blank.
Anything you compare with NULL will result in Unknown (NULL).
Please check this to clear your doubts. To have the correct result, use IS NULL or IS Not NULL as you already know.

Comparing 2 Columns in same table

I need to compare 2 columns in a table and give 3 things:
Count of rows checked (Total Rows that were checked)
Count of rows matching (Rows in which the 2 columns matched)
Count of rows different (Rows in which the 2 columns differed)
I've been able to get just rows matching using a join on itself, but I'm unsure how to get the others all at once. The importance of getting all of the information at the same time is because this is a very active table and the data changes with great frequency.
I cannot post the table schema as there is a lot of data in it that is irrelevant to this issue. The columns in question are both int(11) unsigned NOT NULL DEFAULT '0'. For purposes of this, I'll call them mask and mask_alt.
select
count(*) as rows_checked,
sum(col = col2) as rows_matching,
sum(col != col2) as rows_different
from table
Note the elegant use of sum(condition).
This works because in mysql true is 1 and false is 0. Summing these counts the number of times the condition is true. It's much more elegant than case when condition then 1 else 0 end, which is the SQL equivalent of coding if (condition) return true else return false; instead of simply return condition;.
Assuming you mean you want to count the rows where col1 is or is not equal to col2, you can use an aggregate SUM() coupled with CASE:
SELECT
COUNT(*) AS total,
SUM(CASE WHEN col = col2 THEN 1 ELSE 0 END )AS matching,
SUM(CASE WHEN col <> col2 THEN 1 ELSE 0 END) AS non_matching
FROM table
It may be more efficient to get the total COUNT(*) in a subquery though, and use that value to subtract the matching to get the non-matching, if the above is not performant enough.
SELECT
total,
matching,
total - matching AS non_matching
FROM
(
SELECT
COUNT(*) AS total,
SUM(CASE WHEN col = col2 THEN 1 ELSE 0 END )AS matching
FROM table
) sumtbl