Avoid division by zero in PostgreSQL - sql

I'd like to perform division in a SELECT clause. When I join some tables and use aggregate function I often have either null or zero values as the dividers. As for now I only come up with this method of avoiding the division by zero and null values.
(CASE(COALESCE(COUNT(column_name),1)) WHEN 0 THEN 1
ELSE (COALESCE(COUNT(column_name),1)) END)
I wonder if there is a better way of doing this?

You can use NULLIF function e.g.
something/NULLIF(column_name,0)
If the value of column_name is 0 - result of entire expression will be NULL

Since count() never returns NULL (unlike other aggregate functions), you only have to catch the 0 case (which is the only problematic case anyway). So, your query simplified:
CASE count(column_name)
WHEN 0 THEN 1
ELSE count(column_name)
END
Or simpler, yet, with NULLIF(), like Yuriy provided.
Quoting the manual about aggregate functions:
It should be noted that except for count, these functions return a
null value when no rows are selected.

I realize this is an old question, but another solution would be to make use of the greatest function:
greatest( count(column_name), 1 ) -- NULL and 0 are valid argument values
Note:
My preference would be to either return a NULL, as in Erwin and Yuriy's answer, or to solve this logically by detecting the value is 0 before the division operation, and returning 0. Otherwise, the data may be misrepresented by using 1.

Another solution avoiding division by zero, replacing to 1
select column + (column = 0)::integer;

If you want the divider to be 1 when the count is zero:
count(column_name) + 1 * (count(column_name) = 0)::integer
The cast from true to integer is 1.

Related

SQL: AVG with NULL Values

As far as I understood, the AVG() function ignores NULL Values.
So AVG(4,4,4,4,4,NULL) --> 4
In my case I don't want this to happen.
I need a solution like that: AVG(4,4,4,4,4,NULL) --> 3,33
without replacing the NULL values directly in the table itself.
Is there any way to do this?
Use coalesce() to return the real value of zero for null columns:
select avg(coalesce(some_column, 0))
from ...
You are correct about the behavior of AVG - use COALESCE to convert the NULLs to 0 in the aggregate.
See this answer in "Why SUM(null) is not 0 in Oracle?"
If you are looking for a rationale for this behaviour, then it is to be found in the ANSI SQL standards which dictate that aggregate operators ignore NULL values.
The relevant code is then, simply:
Avg(Coalesce(col,0))
You can use coalesce or nvl also.
Since coalesce examples are already given above, here is an example of nvl
Avg(Nvl(Column_Name),0)
Coalesce, Nvl, Nvl2 functions are handy in such cases of handling null values.
You should look up some examples and documentation on these.
NVL, NVL2, or COALESCE could be used to solve this issue.
like this
select avg(4,4,4,4, nvl(null, 0 )) from dual;
Another possibility is to calculate average using aggregate functions SUM with COUNT
SELECT SUM(column_with_value) / COUNT(primary_column) FROM ...

Set a maximum value for an expression

I am trying to create a score based on the char_length of a field. I am using a fraction of the char_length returned.
(CASE WHEN (char_length(e.summary)/100) is null THEN +0
ELSE +(char_length(e.summary)/100) END)
I would like to know how to set a maximum return value. Is there a simple function or do I need to do one more CASE WHEN with if >=10 THEN ... ?
Simplify to:
LEAST(COALESCE(length(e.summary)/100, 0), 10)
Use LEAST() to introduce an upper border as #Mark already mentioned.
Use COALESCE() to provide a default for NULL values.
length() does the same as char_length()
You can use the least function.
least(<YOUR CALC HERE>, 10) limits the maximum value returned to 10.

Applying the MIN aggregate function to a BIT field

I want to write the following query:
SELECT ..., MIN(SomeBitField), ...
FROM ...
WHERE ...
GROUP BY ...
The problem is, SQL Server does not like it, when I want to calculate the minimum value of a bit field it returns the error Operand data type bit is invalid for min operator.
I could use the following workaround:
SELECT ..., CAST(MIN(CAST(SomeBitField AS INT)) AS BIT), ...
FROM ...
WHERE ...
GROUP BY ...
But, is there something more elegant? (For example, there might be an aggregate function, that I don't know, and that evaluates the logical and of the bit values in a field.)
One option is MIN(SomeBitField+0). It reads well, with less noise (which I would qualify as elegance).
That said, it's more hack-ish than the CASE option. And I don't know anything about speed/efficiency.
Since there are only two options for BIT, just use a case statement:
SELECT CASE WHEN EXISTS (SELECT 1 FROM ....) THEN 1 ELSE 0 END AS 'MinBit'
FROM ...
WHERE ...
This has the advantage of:
Not forcing a table scan (indexes on BIT fields pretty much never get used)
Short circuiting TWICE (once for EXISTS and again for the CASE)
It is a little more code to write but it shouldn't be terrible. If you have multiple values to check you could always encapsulate your larger result set (with all the JOIN and FILTER criteria) in a CTE at the beginning of the query, then reference that in the CASE statements.
This query is the best solution:
SELECT CASE WHEN MIN(BitField+0) = 1 THEN 'True' ELSE 'False' END AS MyColumn
FROM MyTable
When you add the BitField+0 it would automatically becomes like int
select min(convert(int, somebitfield))
or if you want to keep result as bit
select convert(bit, min(convert(int, somebitfield)))
Try the following
Note: Min represent And aggregate function , Max represent Or aggregate function
SELECT ..., MIN(case when SomeBitField=1 then 1 else 0 end), MIN(SomeBitField+0)...
FROM ...
WHERE ...
GROUP BY ...
same result
This small piece of code has always worked with me like a charm:
CONVERT(BIT, MIN(CONVERT(INT, BitField))) as BitField
AVG(CAST(boolean_column AS FLOAT)) OVER(...) AS BOOLEAN_AGGREGATE
Give a fuzzy boolean :
1 indicate that's all True;
0 indicate that's all false;
a value between ]0..1[ indicate partial matching and can be some percentage of truth.

Sql Server: CASE Statement does unexpected behavior when comparing to NULL

Given:
The following Select statement:
select case NULL
when NULL then 0
else 1
end
Problem:
I'm expecting this to return 0 but instead it returns 1. What gives?
Generally speaking, NULL is not something you should attempt to compare for equality, which is what a case statement does. You can use "Is NULL" to test for it. There is no expectation that NULL != NULL or that NULL = NULL. It's an indeterminate, undefined value, not a hard constant.
-- To encompass questions in the comments --
If you need to retrieve a value when you may encounter a NULL column, try this instead:
Case
When SomeColumn IS NULL
Then 0
Else 1
End
I believe that should work. As far as your original post is concerned:
Select Case NULL
When NULL then 0 // Checks for NULL = NULL
else 1 // NULL = NULL is not true (technically, undefined), else happens
end
The trouble is that your Case select automatically attempts to use equality operations. That simply doesn't work with NULL.
I was going to add this as a comment to Aaron's answer, but it was getting too long, so I'll add it as another (part of the) answer.
The CASE statement actually has two distinct modes, simple and searched.
From BOL:
The CASE expression has two formats:
The simple CASE expression compares an expression to a set of simple expressions to determine the result.
The searched CASE expression evaluates a set of Boolean expressions to determine the result.
When the simple CASE (your example) does what it describes as comparison it does an equality comparison - i.e. =
This is clarified in the later documentation:
The simple CASE expression operates by comparing the first expression
to the expression in each WHEN clause for equivalency. If these
expressions are equivalent, the expression in the THEN clause will be
returned.
Allows only an equality check.
Because anything = NULL is always false in ANSI SQL (and if you didn't know this, you need to read up on NULLs in SQL more generally, particularly also with the behavior in the other searched comparison - WHERE x IN (a, b, c)), you cannot use NULL in a simple case and have it ever be compared to a value, with a NULL either in the initial expression or in the list of expressions to be compared against.
If you want to check for NULL, you will have to use an IF/ELSE construct or the searched CASE with a full expression.
I agree that it's kind of unfortunate there is no version which supports an IS comparison to make it easier to write:
select case colname
when IS NULL then 0
else 1
end
Which would make writing certain long CASE statements easier:
select case colname
when IS NULL then ''
when 1 then 'a'
when 2 then 'b'
when 3 then 'c'
when 4 then 'd'
else 'z'
end
But that's just wishful thinking...
An option is to use ISNULL or COALESCE:
select case COALESCE(colname, 999999) -- 999999 is some value never used
when 999999 then ''
when 1 then 'a'
when 2 then 'b'
when 3 then 'c'
when 4 then 'd'
else 'z'
end
But it isn't always a great option.
In addition to the other answers, you need to change the syntax for CASE slightly to do this:
SELECT CASE
WHEN NULL IS NULL THEN 0
ELSE 1
END;
Using the value in your syntax implicitly uses an equals comparison. NULL is unknown, and so is NULL = NULL, so with your current code you will always get zero 1 (geez I did it too).
To get the behavior you want, you can use SET ANSI_NULLS ON; however note that this can change other code in ways you may not be able to predict, and the setting is deprecated - so it will stop working at all in a future version of SQL Server (see this SQL Server 2008 doc).
You need to use the IS NULL operator. Standard comparison operators do not work with NULL.
Check out these MSDN articles about Null that may be useful:
IS [NOT] NULL (Transact-SQL)
Null Values

What does this SQL Query mean?

I have the following SQL query:
select AuditStatusId
from dbo.ABC_AuditStatus
where coalesce(AuditFrequency, 0) <> 0
I'm struggling a bit to understand it. It looks pretty simple, and I know what the coalesce operator does (more or less), but dont' seem to get the MEANING.
Without knowing anymore information except the query above, what do you think it means?
select AuditStatusId
from dbo.ABC_AuditStatus
where AuditFrequency <> 0 and AuditFrequency is not null
Note that the use of Coalesce means that it will not be possible to use an index properly to satisfy this query.
COALESCE is the ANSI standard function to deal with NULL values, by returning the first non-NULL value based on the comma delimited list. This:
WHERE COALESCE(AuditFrequency, 0) != 0
..means that if the AuditFrequency column is NULL, convert the value to be zero instead. Otherwise, the AuditFrequency value is returned.
Since the comparison is to not return rows where the AuditFrequency column value is zero, rows where AuditFrequency is NULL will also be ignored by the query.
It looks like it's designed to detect a null AuditFrequency as zero and thus hide those rows.
From what I can see, it checks for fields that aren't 0 or null.
I think it is more accurately described by this:
select AuditStatusId
from dbo.ABC_AuditStatus
where (AuditFrequency IS NOT NULL AND AuditFrequency != 0) OR 0 != 0
I'll admit the last part will never do anything and maybe i'm just being pedantic but to me this more accurately describes your query.
The idea is that it is desireable to express a single search condition using a single expression but it's merely style, a question of taste:
One expression:
WHERE age = COALESCE(#parameter_value, age);
Two expressions:
WHERE (
age = #parameter_value
OR
#parameter_value IS NULL
);
Here's another example:
One expression:
WHERE age BETWEEN 18 AND 65;
Two expressions
WHERE (
age >= 18
AND
age <= 65
);
Personally, I have a strong personal perference for single expressions and find them easier to read... if I am familiar with the pattern used ;) Whether they perform differently is another matter...