Today I was surprised by this case behaviour:
select case when null then true else false end;
case
------
f
I would expect it to return null since a null casted to boolean yelds a null not a false:
select null::boolean is null;
?column?
----------
t
Any comments on the rationale of this behaviour? What Am I missing?
Use something like
case when p.parent_id is null then false else true end as has_parent
In case you (also) need something like
if column is null then 'value'
Use:
COALESCE(column_name, 'replacment for null value') as column_name
In case you still need a case statement then use:
case COALESCE(column_name, 'asdf')
when 'asdf' then true
else false
end as desired_column_name
You're thinking of the CASE expression like it was taking the null as input to a function or operator, where null input generally results in null output:
regress=> SELECT 't'::boolean = NULL::boolean;
bool
------
(1 row)
wheras in fact it behaves like a WHERE clause in terms of null handling:
craig=> SELECT 't' WHERE NULL;
?column?
----------
(0 rows)
In WHERE clauses - and in CASE, a NULL result from a test expression is treated as "not true and therefore false". In some ways it's regrettable that the SQL standard didn't make a NULL result in a WHERE expression an error instead of treating it as false, but that's how it is.
This is yet another painful symptom of NULL's split personality, where the SQL spec can't decide if NULL means "unknown/undefined value" or "the absence of a value", much like the horrible mess with NULLs and aggregates.
You can use 'select' to check DATA column for null:
select
ID,
case (select 1 where DATA is null)
when 1 then 'no data'
else data
end
from ...
As the PostgreSQL documentation states:
If no WHEN condition is true then the value of the case expression is
the result in the ELSE clause. If the ELSE clause is omitted and no condition matches, the result is null.
Postgresql does not cast the output, and since you have an else condition, you're getting false. The next query returns a null value (Since there's no else condition)
select case when null then true end;
The CASE statement you wrote has two branches:
The one, when null then true, will never happen (because null is not equal to true)
And else branch, that will happen when there are no matches in regular when branches.
Example:
CASE WHEN val = 1 THEN 5
WHEN val = 2 THEN 10
ELSE 20 /*all other values, including null*/
END
select case when null is null then null else 1 end
returns null
select case null when then null else 1 end
returns 1
Related
SELECT * FROM Entity e WHERE e.Status <> ANY(ARRAY[1,2,3]);
Here Status is a nullable integer column. Using the above query i am unable to fetch the records whose status value is NULL.
SELECT * FROM Entity e WHERE (e.Status is NULL OR e.Status = 4);
This query does the trick. Could someone explain me why the first query was not working as expected.
NULL kinda means "unknown", so the expressions
NULL = NULL
and
NULL != NULL
are neither true nor false, they're NULL. Because it is not known whether an "unknown" value is equal or unequal to another "unknown" value.
Since <> ANY uses an equality test, if the value searched in the array is NULL, then the result will be NULL.
So your second query is correct.
It is spelled out in the docs Array ANY:
If the array expression yields a null array, the result of ANY will be null. If the left-hand expression yields null, the result of ANY is ordinarily null (though a non-strict comparison operator could possibly yield a different result). Also, if the right-hand array contains any null elements and no true comparison result is obtained, the result of ANY will be null, not false (again, assuming a strict comparison operator). This is in accordance with SQL's normal rules for Boolean combinations of null values.
FYI:
e.Status is NULL OR e.Status = 4
can be shortened to:
e_status IS NOT DISTINCT FROM 4
per Comparison operators.
In my Postgres database, I'm checking user answers for correctness by checking if two IDs, "user_answered_id" and "expected_answer_id", are equivalent. If the user doesn't provide a "user_answered_id", then we still mark their answer as incorrect.
In Postgres, the following queries
select case when 1 != null then TRUE else FALSE end as test;
select case when 1 = null then TRUE else FALSE end as test;
both result in FALSE. This is true for any number check (e.g., when 2 != null, when 3 != null, ..., etc.
Why doesn't CASE WHEN show TRUE for 1 != null?
Must I put in the check "or is null"? E.g.,
CASE WHEN
user_answered_id != expected_answer_id
OR user_answered_id IS NULL
THEN TRUE
ELSE FALSE
END as user_incorrect_tally
What you are looking for is: IS DISTINCT FROM
select 2 is distinct from null;
?column?
----------
t
select 2 is distinct from 1;
?column?
----------
t
From the docs:
datatype IS DISTINCT FROM datatype → boolean
Not equal, treating null as a comparable value.
1 IS DISTINCT FROM NULL → t (rather than NULL)
NULL IS DISTINCT FROM NULL → f (rather than NULL)
SQL uses three-valued logic: true, false, and null. Null is not false. Null can be thought of as "no value".
Operations on null almost always yield null. So 1 != null is null. 1 = null is null. null = null is null. 5 < null is null. Etc.
To check for null, use is null and is not null.
Back to your query. is not distinct from and is distinct from are like = and != which treat null as a comparable value. So null is distinct from 1 will be true.
select
user_answered_id is distinct from expected_answer_id as user_incorrect
If you need to convert a null into a different value such as 0 or an empty string, use coalesce.
select
coalesce(user_answered_text, 'No Answer')
Your column is named "tally", but a tally means a count. If you intend to count a user's true and false answers use count with a filter.
select
count(user_answered_id) filter (
where user_answered_id = expected_answer_id
) as user_correct_tally,
-- count ignores null, this will only be the questions they tried to answer
count(user_answered_id) as user_answered_tally,
count(user_answered_id) filter (
where user_answered_is is distinct from expected_answer_id
) as user_incorrect_tally
Yes, You should check NULL value with is null, And last query you wrote is correct.
I suggest you to read below documents:
https://www.postgresql.org/docs/current/functions-comparison.html
I have a postgresql query in which I am applying case condition for a boolean, int and date but it is throwing error when i add else '', is there a way to add empty value.
select case when isActive then 'Y' else '' end as active,
case when id is not null else '' end as id,
case when createdDt is not null else '' end dt from table
can anyone help me in this.
Apart from the syntax error in your CASE expression, you cannot use an empty string as boolean value. boolean only has the three values TRUE, FALSE and NULL. Perhaps you could use the latter value, which means “unknown”.
Msg 8114, Level 16, State 5, Line 1 Error converting data type varchar to numeric.
Below is the code that I am using and the schema for the fields mentioned in the code. Some context behind this, I have a set list of indcodes. I only give one below but there are 350 in total. It is quite likely when i query by indcode, area, and ownership that the data does not exist. Hence, why I have the isnull language. Finally, I need rows that contains a value of suppress = 1 to show up as N/A.
For every indcode that I query, I need a result. The result could be actual data or some text (NULL, N/A, etc).
Last is the intended result. I only gave code for one indcode but have included six to give the example some depth.
select
CASE
WHEN suppress = 0 then isnull (mnth1emp, 0)
ELSE 'N/A'
END as mnth1emp,
CASE
WHEN suppress = 0 THEN isnull (mnth2emp, 0)
ELSE 'N/A'
END as mnth2emp,
CASE
WHEN suppress = 0 THEN isnull (mnth3emp, 0)
ELSE 'N/A'
END as mnth3emp
from dbo.industryimport20172f
where area='000003' and indcode='21' and ownership='00'
suppress char (1)
mnth1emp numeric(9,0)
mnth2emp numeric(9,0)
mnth3emp numeric(9,0)
area char(6)
indcode char (6)
ownership char(2)
123 456 789
1 2 3
Null null null
2 3 4
3 4 5
Null Null Null
So the first things that jump out here are:
a) While you're asking for numbers to be returned in the case statement, you're then asking for a string when you get to ELSE. Try casting all the fields as varchars and then entering them as strings:
CASE
WHEN cast(suppress as varchar) = '0' then isnull (cast(mnth1emp as varchar), '0')
ELSE 'N/A'
END as mnth1e
You may not need to cast suppress.
b) Does supress ever have anything other than a number in it? If so, you would need to put the zero as a string:
CASE WHEN supress = '0' --etc.
I am using python3.6 and py-postgresql==1.2.1.
I have the following statement:
db.prepapre("SELECT * FROM seasons WHERE user_id=$1 AND season_id=$2 LIMIT 1), where season_id can be NULL.
I want to be able to be able to get the latest record with a NULL season_id by passing None as the $2 param, but it does not work. Instead, I need to create this second statement:
db.prepapre("SELECT * FROM seasons WHERE user_id=$1 AND season_id IS NULL LIMIT 1)
It must have something to do with season_id = NULL not working and season_id IS NULL is, but is there a way to make this work?
From Comparison Functions and Operators:
Do not write expression = NULL because NULL is not “equal to” NULL. (The null value represents an unknown value, and it is not known whether two unknown values are equal.)
Some applications might expect that expression = NULL returns true if expression evaluates to the null value. It is highly recommended that these applications be modified to comply with the SQL standard. However, if that cannot be done the transform_null_equals configuration variable is available. If it is enabled, PostgreSQL will convert x = NULL clauses to x IS NULL.
and:
19.13.2. Platform and Client Compatibility
transform_null_equals (boolean)
When on, expressions of the form expr = NULL (or NULL = expr) are treated as expr IS NULL, that is, they return true if expr evaluates to the null value, and false otherwise. The correct SQL-spec-compliant behavior of expr = NULL is to always return null (unknown). Therefore this parameter defaults to off.
You could rewrite your query:
SELECT *
FROM seasons
WHERE user_id = $1
AND (season_id = $2 OR ($2 IS NULL AND season_id IS NULL))
-- ORDER BY ... --LIMIT without sorting could be dangerous
-- you should explicitly specify sorting
LIMIT 1;