Adding columns in MySQL - how to treat NULL as 0 - mysql

I have simple table (test) where I need to perform some additions (I want to get a row total and a column total).
id var1 var2
1 NULL NULL
2 10 NULL
For column totals, summing works as expected (NULL is ignored in the addition):
SELECT SUM(var1) FROM test
10
For row totals, addition does not ignore NULL (if any column is NULL the result is NULL):
SELECT var1+var2 FROM test
NULL
NULL
What I want it to return is:
SELECT var1+var2 FROM test
NULL
10
Is there a way to get MySQL to treat NULL as 0 in an addition?

You want to use coalesce():
select coalesce(var1, 0) + coalesce(var2, 0)
coalesce() is ANSI standard and available in most databases (including MySQL).

use the IFNULL function
SELECT IFNULL(var1, 0) + IFNULL(var2, 0) FROM test

select
Case when (coalesce(`var1`, 0) + coalesce(`var2`, 0))=0
then NULL
else coalesce(`var1`, 0) + coalesce(`var2`, 0)
end
from test
SQL FIDDLE

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 Case function behave strange and inconstent

We are using MySQL 8 as our java application DB.
We have a query with the following format:
select
id,
group_concat(NAME ORDER BY ID separator ',,') AS Code,
CASE
WHEN MAX(p.VARIABLEfactor) = 1 THEN MAX(i.factor) ELSE MAX(p.factor) END AS factor
from MA_TABLE
join TABLE_P p on (...)
join TABLE_I i on (...)
group by id
The query worked very fine in our development environments until deploy with client where the factor column is getting null.
We have run the same query in the client environment from MySQL Workbench and we can see that the factor column is getting well populated.
After some debugging,we changed :
CASE
WHEN MAX(p.VARIABLEfactor) = 1 THEN MAX(i.factor) ELSE MAX(p.factor) END AS factor
to
MAX(
WHEN p.VARIABLEfactor = 1 THEN i.factor ELSE p.factor END ) AS factor,
and the query worked correctly.
Any help here please?
From your explanation I gather that you don't understand the difference of your two case expressions. But they are very different. Let's look at an example for one ID:
ID
VARIABLEfactor
i.factor
p.factor
100
0
null
10
100
1
null
20
Your expression
CASE WHEN MAX(p.VARIABLEfactor) = 1 THEN MAX(i.factor) ELSE MAX(p.factor) END
looks at the maximum VARIABLEfactor, which is 1, so the THEN case applies and the maximum i.factor is returned. This is null, as all i.factor are null.
Your expression
MAX(WHEN p.VARIABLEfactor = 1 THEN i.factor ELSE p.factor END)
looks at each row's VARIABLEfactor. For the first row this is 0, so the ELSE case applies and p.factor 10 is used. For the second row the VARIABLEfactor is 1, so its i.factor null gets used. Of these you take the maximum, which is 10.
To recap: The first expression is just a CASE expression on the aggregation results. It returns null here. The second expression is a conditional aggregation. It returns 10 for the sample data.

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.

Get the earliest time if rows are more than 1

I need help with some MySQL pain in the ****... Anyway, i got the following sql :
SELECT id,count(*),
CASE
WHEN count(*) > 1 THEN
// I need the minimal `taskdate_time` column from the selected rows
// where a certain boolean is active
ELSE taskdate_time
END
FROM timehistory th
WHERE `send`=true
GROUP BY date_format(taskdate_time, "%Y-%m-%d"), user_id
As described in the comments, i need to get the earliest time out for the two rows where a column called removed is not FALSE
How do i achieve this?
My columns are :
`id` - int
`taskdateuser_id` int
`user_id` int
`changed_by` int
`batch_id` int
`taskdate_time` timestamp
`send` tinyint
`isread` tinyint
`update` tinyint
`removed` tinyint
Many thanks in advance!!!
EDIT:
I might explain it a bit more. If i got the following table rows :
The red marked rows are captured by the CASE count(*) > 1, because there are 2 rows returned by the group by. Then i need to to a SELECT from that 2 captured rows where removed=false and min(taskdate_time). So if 4 rows are returned for that group by, and 2 of the rows are removed=false and the other are removed=true then i need to do a subselect for the minimum taskdate_time that 2 rows where removed=false.
SELECT id,
count(*),
CASE WHEN count(*) > 1
THEN (SELECT MAX(taskdate_time) FROM timehistory f WHERE f.id = th.id AND removed = 0)
ELSE taskdate_time
END
FROM timehistory th
WHERE `send` = true
GROUP BY date_format(taskdate_time, "%Y-%m-%d"), user_id
You could try something like this:
SELECT TH.user_id, COUNT(*),
CASE WHEN COUNT(*) > 1
THEN MIN(IF(TH.removed, TH.taskdate_time, NULL))
ELSE TH.taskdate_time
END
FROM TimeHistory TH
...
Sample Fiddle Demo
However, if COUNT > 1 AND there aren't any records where TH.removed is true, then this will return NULL for that value. What should it return in those cases?
--EDIT--
In response to comments, then this should work just wrapping it with COALESCE:
COALESCE(
CASE
WHEN COUNT(*) > 1
THEN MIN(IF(TH.removed, TH.taskdate_time, NULL))
ELSE TH.taskdate_time
END, MIN(TH.taskdate_time))