Having a table
CREATE TABLE t (x int)
INSERT INTO t VALUES (null), (0) ,(42)
And query with two placeholders:
SELECT x FROM t WHERE x = ? OR x IS NULL AND ? IS NULL
The logic is the following: if I resolve those placeholders with 42 it returns 42. If I resolve them with null it returns null. In other words, it is a null-including search.
The question is:
Is it possible to rewrite this query (in Postgresql) to have a single placeholder ? instead of two?
Postgres implements null-safe equality through operator IS [NOT] DISTINCT FROM, which is exactly what you ask for.
So:
SELECT x FROM t WHERE x IS NOT DISTINCT FROM ?
The placeholder for parameters in PostgreSQL is $1, $2, etc. Letting you use ? instead is something some drivers implement for convenience, but they do offer less flexibility.
Using the real notation, you can specify one parameter to occur in more than one place:
SELECT x FROM t WHERE x = $1 OR x IS NULL AND $1 IS NULL
This has the advantage over IS NOT DISTINCT FROM in that it can use an index.
Yes. You can use is not distinct from and only use one placeholder:
where x is not distinct from ?
is distinct from/is not distinct from are null-safe comparison operators that treat null as a "real" value for comparison purposes (so null = null for instance).
Related
I am building a SQL query inside a function. The functions parameters are my criterias for the WHERE clause. Parameters can be null too.
foo (1,2,3) => SELECT x FROM y WHERE a=1 AND b=2 AND c=3;
foo (null, 2, null) => SELECT x FROM y WHERE b=2;
My approach to do that in code is to add a very first alltime true in the WHERE-clause (e.g. 1=1 or NULL is NULL or 2 > 1)
So I do not need to handle the problem that the 1st WHERE condition is after a "WHERE" and all others are after a "AND".
String sql="SELECT x FROM y WHERE 1=1";
if (a!=null)
{
sql += " AND a="+a;
}
Is there a better term than 1=1 or my other samples to EXPLICITLY have a always true value? TRUE and FALSE is not working.
Oracle does not support a boolean type in SQL. It exists in PL/SQL, but can't be used in a query. The easiest thing to do is 1=1 for true and 0=1 for false. I'm not sure if the optimizer will optimize these out or not, but I suspect the performance impact is negligible. I've used this where the number of predicates is unknown until runtime.
I think this construct is superior to anything using nvl, coalesce, decode, or case, because those bypass normal indexes and I've had them lead to complicated and slow execution plans.
So, yes I'd do something like you have (not sure what language you're building this query in; this is not valid PL/SQL. Also you should use bind variables but that's another story):
sql = "SELECT x FROM y WHERE 1=1";
if (a != null)
{
sql += " AND a=" + a;
}
if (b != null)
{
sql += " AND b=" + b;
}
... etc.
I think this is better than Gordon Linoff's suggestion, because otherwise this would be more complicated because you'd have to have another clause for each predicate checking if there was a previous predicate, i.e. whether you needed to include the AND or not. It just makes the code more verbose to avoid a single, trivial clause in the query.
I've never really understood the approach of mandating a where clause even when there are no conditions. If you are constructing the logic, then combine the conditions. If the resulting string is empty, then leave out the where clause entirely.
Many application languages have the equivalent of concat_ws() -- string concatenation with a separator (join in Python, for instance). So leaving out the where clause does not even result in code that is much more complicated.
As for your question, Oracle doesn't have boolean values, so 1=1 is probably the most common approach.
use NVL so you can set a default value. Look up NVL or NVL2. Either might work for you.
check out: http://www.dba-oracle.com/t_nvl_vs_nvl2.htm
In sql the standard way to "default null" is to use coalesce. So lets say your input is #p1, #p2, and #p3
Then
select *
from table
where a = coalesce(#p1,a) and
b = coalesce(#p2,b) and
c = coalesce(#p3,c)
Thus if (as an example) #p1 is null then it will compare a = a (which is always true). If #p1 is not null then it will filter on a equals that value.
In this way null becomes a "wildcard" for all records.
you could also use decode to get the results you need since it is oracle
Select
decode(a, 1, x,null),
decode(b, 2, x,null),
decode(c, 3, x,null)
from y
What about using OR inside of the parentheses while ANDs exist at outside of them
SELECT x
FROM y
WHERE ( a=:prm1 OR :prm1 is null)
AND ( b=:prm2 OR :prm2 is null)
AND ( c=:prm3 OR :prm3 is null);
When comparing boolean values in PostgreSQL we use the comparison operator =.
This is all well but when I want to check against matching NULL values what should I use?
I have used IS NOT DISTINCT FROM when comparing integers that have to match if both are NULL but is it a correct way to go when dealing with boolean values?
I have used IS NOT DISTINCT FROM when comparing integers that have to match if both are NULL but is it a corract way to go when dealing with boolean values?
I see no reason why not. :)
x IS NOT DISTINCT FROM y is basically short-hand for (x = y) OR (x IS NULL AND y IS NULL)
Use is null clause. Like this:
select * from my_table where my_booleae_field is null
Try using coalesce
Like:
where coalesce(your_field, true) = true
Then null values and boolean true will pass the statement
I am using a tool to produce SQL queries and I need to filter one of the queries with a multiple parameters.
The query is similar to this:
Select *
From Products
Where (#ProductTypeIds is null or Product.ProductTypeId in (#ProductTypeIds))
I know the above query is not correct on a traditional SQL, read on..
Essentially, I'm trying to apply a filter where if nothing is passed for #ProductTypeIds parameter, the where condition is not applied.
When multiple parameters are being passed, though, #ProductTypeIds is being translated by the tool into the following query:
Select *
From Products
Where (#ProductTypeIds1, #ProductTypeIds2 is null or Product.ProductTypeId in (#ProductTypeIds1, #ProductTypeIds2))
Which is clearly an invalid query. So I thought I could be clever and use COALESCE to check if they are null:
Select *
From Products
Where (COALESCE(#ProductTypeIds, null) is null or Product.ProductTypeId in (#ProductTypeIds))
This query is being translated correctly, however, now my use of COALESCE throws an error:
At least one of the arguments to COALESCE must be an expression that is not the NULL constant.
How can I efficiently check that #ProductTypeIds (which be being translated into #ProductTypeIds1, #ProductTypeIds2 is all null so I can apply the filter or ignore?
In other words, is there a way to Distinct a list of parameters to check if the final result is null ?
Thanks
I have no idea how your tool works, but try the following.
Instead of checking for null check for the value that will never come in your params like:
WHERE COALESCE(#ProductTypeIds1, #ProductTypeIds2, -666) == -666 OR ...
Using Postgres 9.0, I need a way to test if a value exists in a given array. So far I came up with something like this:
select '{1,2,3}'::int[] #> (ARRAY[]::int[] || value_variable::int)
But I keep thinking there should be a simpler way to this, I just can't see it. This seems better:
select '{1,2,3}'::int[] #> ARRAY[value_variable::int]
I believe it will suffice. But if you have other ways to do it, please share!
Simpler with the ANY construct:
SELECT value_variable = ANY ('{1,2,3}'::int[])
The right operand of ANY (between parentheses) can either be a set (result of a subquery, for instance) or an array. There are several ways to use it:
SQLAlchemy: how to filter on PgArray column types?
IN vs ANY operator in PostgreSQL
Important difference: Array operators (<#, #>, && et al.) expect array types as operands and support GIN or GiST indices in the standard distribution of PostgreSQL, while the ANY construct expects an element type as left operand and can be supported with a plain B-tree index (with the indexed expression to the left of the operator, not the other way round like it seems to be in your example). Example:
Index for finding an element in a JSON array
None of this works for NULL elements. To test for NULL:
Check if NULL exists in Postgres array
Watch out for the trap I got into: When checking if certain value is not present in an array, you shouldn't do:
SELECT value_variable != ANY('{1,2,3}'::int[])
but use
SELECT value_variable != ALL('{1,2,3}'::int[])
instead.
but if you have other ways to do it please share.
You can compare two arrays. If any of the values in the left array overlap the values in the right array, then it returns true. It's kind of hackish, but it works.
SELECT '{1}' && '{1,2,3}'::int[]; -- true
SELECT '{1,4}' && '{1,2,3}'::int[]; -- true
SELECT '{4}' && '{1,2,3}'::int[]; -- false
In the first and second query, value 1 is in the right array
Notice that the second query is true, even though the value 4 is not contained in the right array
For the third query, no values in the left array (i.e., 4) are in the right array, so it returns false
unnest can be used as well.
It expands array to a set of rows and then simply checking a value exists or not is as simple as using IN or NOT IN.
e.g.
id => uuid
exception_list_ids => uuid[]
select * from table where id NOT IN (select unnest(exception_list_ids) from table2)
Hi that one works fine for me, maybe useful for someone
select * from your_table where array_column ::text ilike ANY (ARRAY['%text_to_search%'::text]);
"Any" works well. Just make sure that the any keyword is on the right side of the equal to sign i.e. is present after the equal to sign.
Below statement will throw error: ERROR: syntax error at or near "any"
select 1 where any('{hello}'::text[]) = 'hello';
Whereas below example works fine
select 1 where 'hello' = any('{hello}'::text[]);
When looking for the existence of a element in an array, proper casting is required to pass the SQL parser of postgres. Here is one example query using array contains operator in the join clause:
For simplicity I only list the relevant part:
table1 other_name text[]; -- is an array of text
The join part of SQL shown
from table1 t1 join table2 t2 on t1.other_name::text[] #> ARRAY[t2.panel::text]
The following also works
on t2.panel = ANY(t1.other_name)
I am just guessing that the extra casting is required because the parse does not have to fetch the table definition to figure the exact type of the column. Others please comment on this.
I just had a trivial but genuine use for NULLIF(), for the first time in my career in SQL. Is it a widely used tool I've just ignored, or a nearly-forgotten quirk of SQL? It's present in all major database implementations.
If anyone needs a refresher, NULLIF(A, B) returns the first value, unless it's equal to the second in which case it returns NULL. It is equivalent to this CASE statement:
CASE WHEN A <> B OR B IS NULL THEN A END
or, in C-style syntax:
A == B || A == null ? null : A
So far the only non-trivial example I've found is to exclude a specific value from an aggregate function:
SELECT COUNT(NULLIF(Comment, 'Downvoted'))
This has the limitation of only allowing one to skip a single value; a CASE, while more verbose, would let you use an expression.
For the record, the use I found was to suppress the value of a "most recent change" column if it was equal to the first change:
SELECT Record, FirstChange, NULLIF(LatestChange, FirstChange) AS LatestChange
This was useful only in that it reduced visual clutter for human consumers.
I rather think that
NULLIF(A, B)
is syntactic sugar for
CASE WHEN A = B THEN NULL ELSE A END
But you are correct: it is mere syntactic sugar to aid the human reader.
I often use it where I need to avoid the Division by Zero exception:
SELECT
COALESCE(Expression1 / NULLIF(Expression2, 0), 0) AS Result
FROM …
Three years later, I found a material use for NULLIF: using NULLIF(Field, '') translates empty strings into NULL, for equivalence with Oracle's peculiar idea about what "NULL" represents.
NULLIF is handy when you're working with legacy data that contains a mixture of null values and empty strings.
Example:
SELECT(COALESCE(NULLIF(firstColumn, ''), secondColumn) FROM table WHERE this = that
SUM and COUNT have the behavior of turning nulls into zeros. I could see NULLIF being handy when you want to undo that behavior. If fact this came up in a recent answer I provided. If I had remembered NULLIF I probably would have written the following
SELECT student,
NULLIF(coursecount,0) as courseCount
FROM (SELECT cs.student,
COUNT(os.course) coursecount
FROM #CURRENTSCHOOL cs
LEFT JOIN #OTHERSCHOOLS os
ON cs.student = os.student
AND cs.school <> os.school
GROUP BY cs.student) t