What does a zero value coalesce function do? - mysql

I have been exploring dbt tools and I came across the following code snippet :
coalesce(customer_orders.number_of_orders, 0) as number_of_orders
I understand that a coalesce function is used to return the first non-null value in a list. What I do not understand is what does the zero in the second parameter signify?

The COALESCE function returns the first non-null value in a list. COALESCE can take n number of arguments.
COALESCE(val1, val2, ...., val_n)
So according to the query:
coalesce(customer_orders.number_of_orders, 0) as number_of_orders
In case customer_orders.number_of_orders is NULL the result returned in number_of_orders would be 0.

COALESCE can use as many arguments as you want. In most cases (like in your example), COALESCE(some_column,0) is used to prevent that creating a sum or building an average will not lead to the desired result.
Assume there are three columns and you want to sum them. In case you don't use COALESCE, the sum will be NULLeven if only one of your three columns is NULL. So you will use COALESCEand replace NULL values by zero in order to receive the sum of all NOT NULL values.
You can "translate" COALESCE into a CASE WHEN construct:
COALESCE(column1,0)
does following:
CASE WHEN column1 IS NULL THEN 0 ELSE column1 END
Another use case of COALESCE is to replace column1 by column2 if column1 is NULL, if also column2 is NULL, take column3 etc.
I created an example here, so you can see what I mean:
db<>fiddle

Related

Multiple COALESCE in GROUP_CONCAT - is showing value if only first column isn't null

Two table's purchase and sales 's column date column has been concatenated as date row.
GROUP_CONCAT(purchase.date,',',sales.date) AS date
But both of them can be blank, according to data input.
So I have concatenated as
GROUP_CONCAT(COALESCE(purchase.date,''),',',COALESCE(sales.date,''))AS date
Here, why if purchase.date is empty and sales.date has value, sales.date column is also fetched as empty ? but in case of purchase.date, value is shown even if sales.date is empty.
I mean my code is not working for second column of GROUP_CONCAT. Second column value is only showed if first one is not empty.
I tried with
GROUP_CONCAT(COALESCE(purchase.date,',',sales.date),'')AS date
GROUP_CONCAT(COALESCE(purchase.date,',',sales.date,'')AS date
but still second column is showing null though it has value. if I move sales.date in first position, it shows value.
Here is fiddle. date shows value because, sales.date is in first position, date1 returns empty because sales.date is in second position.
Sql Fiddle
This does not do what you seem to want it to do:
COALESCE(sales.date, ',', purchase.date)
COALESCE() returns the first of the three values that is not NULL, so you will never get purchase.date: ',' is never NULL.
You basically want:
GROUP_CONCAT(purchase.date, ',', sales.date) AS date
However, you have learned that CONCAT() -- and even this implicit concatenate -- return NULL if any of the values are NULL. One solution is individual COALESCE() -- as in your answer. I cannot reproduce your problem. Here is an example of the code working.
Or, you can use CONCAT_WS() (although you miss the separator):
GROUP_CONCAT(CONCAT_WS(',', sales.date, purchase.date))
I am not sure only COALESCE is enough here, as #TimBiegeleisen said - COALESCE always returns the first non NULL value in its list. So I apply CASE condition
CASE
WHEN purchase.date IS NULL THEN GROUP_CONCAT(COALESCE(sales.date,''))
WHEN sales.date IS NULL THEN GROUP_CONCAT(COALESCE(purchase.date,''))
WHEN purchase.date !='' AND sales.date !='' THEN GROUP_CONCAT(COALESCE(purchase.date,',',sales.date),'')
END AS date1

MySQL comparison of date to the max of a subquery that might return null

I have a WHERE CLAUSE that looks like this:
t.triggered > (SELECT MAX(time_flag) FROM triggers)
When the subquery returns a value because there is one the whole query is executed normally.
When the subquery has no returned value the comparison is always false and no records are returned even though everything is greater than 'NULL'.
I read that 'NULL' is not appropriate for comparisons, so how can the query be written in order to overcome this issue?
Something like:
t.triggered > COALESCE((SELECT MAX(time_flag) FROM triggers), '1900-01-01')
A predicate containing a NULL value, doesn't evaluate to either true, or false: it evaluates to NULL. Using COALESCE you can compare to a very old value in case the subquery returns NULL and hence return everything from your table.
Replace NULL value with default value (some minimal date) in subquery using IFNULL or COALESCE functions:
t.triggered > (SELECT IFNULL(MAX(time_flag), '1000-01-01') FROM triggers)

COUNTNULL() as a MySQL function. How would it operate?

I am sure it would be an aggregate function because it is going to count a collection of data.
However, how does any COUNT() function operate in MySQL to perform its respective actions?
Not 100% clear what you are looking for, but for selecting a count of null values in a column, I use something like this:
SELECT SUM(CASE WHEN columnname IS NULL THEN 1 ELSE 0 END) FROM tablename;
When the value is NULL, it is assigned the value 1 otherwise 0, then summed over whatever aggregate you need.
The COUNT(*) is an aggregate function. In the SELECT list, the expression COUNT(*) will return a count of rows. Without a GROUP BY clause, all rows will be collapsed into a single row, and the COUNT(*) aggregate will contain a non-negative integer value representing the number of rows that were collapsed... a "count" of the number of rows.
As you seem to be aware, other expressions involving the COUNT() aggregate operate a little differently, with respect to NULL values.
In the SELECT list, an expression COUNT(expr) operates exactly like COUNT(*) except for rows with values of expr that evaluate to NULL are not included in the count.
This all operates according to the specification.
As far as the non-existent COUNTNULL() function, it depends what you want that to achieve. If you wanted to get a count of the rows that had a NULL value for an expression, you could perform a conditional test, and return a non-NULL value, and use the existing COUNT aggregate, for example:
SELECT COUNT(CASE WHEN expr IS NULL THEN 1 ELSE NULL END) AS `COUNTNULL`
FROM ...
I don't remember where I learned this technique, but arguably the most elegant -- or at least minimalistic -- way to invert the logic of COUNT() is with this expression, which admittedly gives a first impression that black magic may somehow be involved... but it's perfectly legitimate:
COUNT(column1 IS NULL OR NULL)
...this correctly counts only the rows where column1 is null, because it is equivalent to the following expression...
COUNT( (column1 IS NULL) OR (NULL) )
It's a boolean expression that can only ever evaluate to 1 ("true," when column1 is null, and this row is thus counted), or NULL (otherwise, so the row will not be counted).
Logically, it's equivalent to the CASE expression offered by #spencer7593.

Mysql SUM inside a CASE

Say I have a query that is something like this:
select
table1.column1,
table1.column2,
...,
case when foo.id is null then 'N/A' else sum(foo.points) end as score -- same result using ifnull()
from
table1 inner join table2 on ...
left join foo on ...
group by ...
Since I do a LEFT JOIN on foo, there is a chance that there is no match. In that case, I want the score to show as 'N/A' instead of NULL or 0. But when I do the above query, I get blob for the score column for all rows.
When you have different data types in the results in the case it will return a value with the data type blob if it can't find a common type.
Try casting the sum to a char:
case when foo.id is null then 'N/A' else cast(sum(foo.points) as char) end as score
If you are grouping, you should really put your sum around the case, like:
sum(case when foo.id is null then 0 else foo.points)
..as otherwise you are summing just the row value (meaning only one value).
Also, a column should only have one data type, so either number or text, which is why you might be seeing this issue since you are trying to display either a number or text in the same column.
If you really want N/A, you can try converting the number to text, and then using the coalesce function to handle nulls, however I would need to see your data to say the best way to write your above query. If you can create an SQL fiddle, I would be more than happy to take a look :)
SUM up the the results of the CASE..WHEN.
SUM(CASE WHEN foo.id IS NULL THEN NULL ELSE foo.points) END AS score
You can display the default value in the frontend application (n/a) when the score field is null (or equals to 0).
The score will be NULL when there all rows has null in foo.id.

NULL to zero with ISNULL

I have the following SQL statement:
SELECT name, SUM(growth) AS sum_buy_price, SUM(recovery) AS sum_msrp, SUM(growth)+SUM(recovery) AS total
FROM orders
WHERE id = ?
GROUP BY name
My data is coming from a CSV file that I have no control over and either 'growth' or 'recovery' can be NULL in the data, but not at the same time. I need to use ISNULL to convert the possible NULL values to zero in order for the SUM to work correctly, but I'm unsure of how/where to add the ISNULL since the SELECT is indexing another record (name).
ISNULL returns whether the argument passed is null (i.e., it is analogous to true or false). I suppose, what you need is IFNULL:
SELECT
name,
SUM(IFNULL(growth, 0)) AS sum_buy_price,
SUM(IFNULL(recovery, 0)) AS sum_msrp,
SUM(IFNULL(growth, 0))+SUM(IFNULL(recovery,0)) AS total
FROM
orders
WHERE
id = ?
GROUP BY
name
The SUM() function ignores NULL values, so you don't need to change a NULL to a 0 in order for it to work properly.
If however, all values that you're aggregating are NULL and you want to return a 0 instead of NULL you can use IFNULL() or the more common COALESCE() to show 0 as the sum instead of NULL:
SELECT COALESCE(SUM(growth),0)
ISNULL() is a valid SQL Server function, IFNULL() is the equivalent in MySQL, but all major databases make use of COALESCE() which returns the first non-NULL value in a set, ie: COALESCE(NULL,NULL,5) would return 5.
This should work for you:
SELECT name, SUM(ISNULL(growth, 0)) AS sum_buy_price,
SUM(ISNULL(recovery, 0)) AS sum_msrp,
SUM(ISNULL(growth, 0))+SUM(ISNULL(recovery,0)) AS total
FROM orders
WHERE id = ?
GROUP BY name