A PostgreSQL query with 'ANY' is not working - arrays

SELECT "Ticket_id" FROM "Tickets"
WHERE "Status" = 1 AND ("Ticket_id" != ANY(array[1,2,3])) Limit 6
And the result is 1,2,3,4,5,6

You want to use ALL, not ANY. From the fine manual:
9.21.3. ANY/SOME (array)
expression operator ANY (array expression)
[...] The left-hand expression is evaluated and compared to each element of the array using the given operator, which must yield a Boolean result. The result of ANY is "true" if any true result is obtained.
So if we say this:
1 != any(array[1,2])
then we'll get true since (1 != 1) or (1 != 2) is true. ANY is essentially an OR operator. For example:
=> select id from (values (1),(2),(3)) as t(id) where id != any(array[1,2]);
id
----
1
2
3
(3 rows)
If we look at ALL, we see:
9.21.4. ALL (array)
expression operator ALL (array expression)
[...] The left-hand expression is evaluated and compared to each element of the array using the given operator, which must yield a Boolean result. The result of ALL is "true" if all comparisons yield true...
so if we say this:
1 != all(array[1,2])
then we'll get false since (1 != 1) and (1 != 2) is false and we see that ALL is essentially an AND operator. For example:
=> select id from (values (1),(2),(3)) as t(id) where id != all(array[1,2]);
id
----
3
(1 row)
If you want to exclude all values in an array, use ALL:
select "Ticket_id"
from "Tickets"
where "Status" = 1
and "Ticket_id" != all(array[1,2,3])
limit 6

Do you mean:
"Ticked_id" NOT IN (1,2,3)

Related

SQL SERVER and NULL values on Equal operator

I have in a where clause the following statement COLUMN_1 <> 'O'
But the rows that contains NULL on the COLUMN_1 are not take in consideration, it is like they contain the value 'O'
Why is that ?
Thanks in advance.
Because NULL does not equal, nor does not not equal anything, including NULL. NULL <> 'O' evaluates to UNKNOWN; which is specifically not TRUE.
If you want to evaluate with NULL values you need to use IS NULL:
WHERE (COLUMN_1 <> 'O' OR COLUMN_1 IS NULL)
This is also documented on both not-equal operator articles:
Not Equal To (Transact SQL) - traditional
Compares two expressions (a comparison operator). When you compare nonnull expressions, the result is TRUE if the left operand is not equal to the right operand; otherwise, the result is FALSE. If either or both operands are NULL, see the topic SET ANSI_NULLS (Transact-SQL).
Not Equal To (Transact SQL) - exclamation
Tests whether one expression is not equal to another expression (a comparison operator). If either or both operands are NULL, NULL is returned. Functions the same as the <> (Not Equal To) comparison operator.

Check if array has value

I'm running this simple check:
select * from mytable
where field_name = any(array['2']::_varchar);
field_name is _varcharso it's an array
but I'm getting this:
ERROR: operator does not exist: character varying[] = character varying
What am I missing?
Thanks!
=ANY unwraps it RHS and compares them individually to the LHS, so it would be the same thing as field_name = '2'::varchar. You can't compare an array to a scalar like that. You want an operator that doesn't unwrapped the argument but compares arrays to each other:
field_name #> array['2']::_varchar
or
field_name && array['2']::_varchar
Or you want to leave the literal as a scalar, and then unwrap the other side which is already an array so it too becomes a scalar:
'2' =ANY (field_name)

Postgres NOT in array

I'm using Postgres' native array type, and trying to find the records where the ID is not in the array recipient IDs.
I can find where they are IN:
SELECT COUNT(*) FROM messages WHERE (3 = ANY (recipient_ids))
But this doesn't work:
SELECT COUNT(*) FROM messages WHERE (3 != ANY (recipient_ids))
SELECT COUNT(*) FROM messages WHERE (3 = NOT ANY (recipient_ids))
What's the right way to test for this condition?
SELECT COUNT(*) FROM "messages" WHERE NOT (3 = ANY (recipient_ids))
You can always negate WHERE (condition) with WHERE NOT (condition)
You could turn it around a bit and say "3 is not equal to all the IDs":
where 3 != all (recipient_ids)
From the fine manual:
9.21.4. ALL (array)
expression operator ALL (array expression)
The right-hand side is a parenthesized expression, which must yield an array value. The left-hand expression is evaluated and compared to each element of the array using the given operator, which must yield a Boolean result. The result of ALL is "true" if all comparisons yield true (including the case where the array has zero elements). The result is "false" if any false result is found.
Beware of NULLs
Both ALL:
(some_value != ALL(some_array))
And ANY:
NOT (some_value = ANY(some_array))
Would work as long as some_array is not null. If the array might be null, then you must account for it with coalesce(), e.g.
(some_value != ALL(coalesce(some_array, array[]::int[])))
Or
NOT (some_value = ANY(coalesce(some_array, array[]::int[])))
From the docs:
If the array expression yields a null array, the result of ANY will be null
If the array expression yields a null array, the result of ALL will be null
Augmenting the ALL/ANY Answers
I prefer all solutions that use all or any to achieve the result, appreciating the additional notes (e.g. about NULLs). As another augementation, here is a way to think about those operators.
You can think about them as short-circuit operators:
all(array) goes through all the values in the array, comparing each to the reference value using the provided operator. As soon as a comparison yields false, the process ends with false, otherwise true. (Comparable to short-circuit logical and.)
any(array) goes through all the values in the array, comparing each to the reference value using the provided operator. As soon as a comparison yields true, the process ends with true, otherwise false. (Comparable to short-circuit logical or.)
This is why 3 <> any('{1,2,3}') does not yield the desired result: The process compares 3 with 1 for inequality, which is true, and immediately returns true. A single value in the array different from 3 is enough to make the entire condition true. The 3 in the last array position is prob. never used.
3 <> all('{1,2,3}') on the other hand makes sure all values are not equal 3. It will run through all comparisons that yield true up to an element that yields false (the last in this case), to return false as the overall result. This is what the OP wants.
an update:
as of postgres 9.3,
you can use NOT in tandem with the #> (contains operator) to achieve this as well.
IE.
SELECT COUNT(*) FROM "messages" WHERE NOT recipient_ids #> ARRAY[3];
not (3 = any(recipient_ids))?
Note that the ANY/ALL operators will not work with array indexes. If indexes are in mind:
SELECT COUNT(*) FROM "messages" WHERE 3 && recipient_ids
and the negative:
SELECT COUNT(*) FROM "messages" WHERE NOT (3 && recipient_ids)
An index can then be created like:
CREATE INDEX recipient_ids_idx on tableName USING GIN(recipient_ids)
Use the following query
select id from Example where NOT (id = ANY ('{1, 2}'))

AND/OR based on variable value in stored procedures

I would like to use AND/OR between the conditions in a stored procedure, and the decision is dependent on the parameter value whether it was 0 (AND) or 1 (OR)
Can anyone help me with this please, i guess this is an easy thing to do but i can't seem to figure it out. Thanks
The easiest way (on first glance) would be to concatenate the query string using dynamic SQL, but dynamic SQL has its issues.
See The Curse and Blessings of Dynamic SQL for an in-depth explanation.
So I would try to avoid dynamic SQL, which is no big deal if your queries are not too complex.
The easiest way is just to fire two different queries depending on the parameter value:
CREATE PROCEDURE spTest
#AndOr bit
AS
BEGIN
if #AndOr = 0 begin
select * from YourTable where foo = 1 and bar = 2
end
else begin
select * from YourTable where foo = 1 or bar = 2
end
END
This is of course an example with a very simple query.
If you have lots of queries, or if your queries are very complex, this might not be the best solution because it forces you to duplicate all queries...but as always, it depends :-)
You can implement your logic on a CASE statement. Something like this:
CREATE PROCEDURE dbo.MySP #OrAnd BIT
AS
BEGIN
SELECT *
FROM MyTable
WHERE CASE WHEN Condition1 AND Condition2 AND #OrAnd = 0 THEN 1
WHEN (Condition1 OR Condition2) AND #OrAnd = 1 THEN 1 ELSE 0 END = 1
END
If you convert the simple conditions' boolean results into numeric ones (0 or 1), you will be able to use your parameter in the following way:
(
(CASE WHEN condition1 THEN 1 ELSE 0 END ^ #AndOr)
&
(CASE WHEN condition2 THEN 1 ELSE 0 END ^ #AndOr)
) ^ #AndOr = 1
Here #AndOr is your parameter, ^ is the Transact-SQL bitwise exclusive OR operator, & stands for the bitwise AND in Transact-SQL, and the CASE expressions are used to convert the boolean results into 0 or 1.
If #AndOr = 0 (which means we want AND between the conditions), the above expression effectively boils down to this:
case1 & case2 = 1
because X XOR 0 yields X and so neither individual values of case1 and case2 nor the entire result of the & operator are not affected by the ^ operators. So, when #AndOr is 0, the result of the original expression would be equivalent to the result of condition1 AND condition2.
Now, if #AndOr = 1 (i.e. OR), then every ^ operator in the expression returns the inverted value of its left operand, in other words, negates the left operand, since 1 XOR 1 = 0 and 0 XOR 1 = 1. Therefore, the original expression would essentially be equivalent to the following:
¬ (¬ case1 & ¬ case2) = 1
where ¬ means negation. Or, converting it back to the booleans, it would be this:
NOT (NOT condition1 AND NOT condition2)
According to one of De Morgan's laws,
(NOT A) AND (NOT B) = NOT (A OR B)
Applying it to the above condition, we get:
NOT (NOT condition1 AND NOT condition2) = NOT (NOT (condition1 OR condition2)) =
= condition1 OR condition2
So, when #AndOr is 1, the expression given in the beginning of my answer is equivalent to condition1 OR condition2. Thus, it works like expected based on the value of #AndOr.
Having the input parameter you can use a IF clause to make different selects.
If input parameter = 0 make the AND conditions, otherwise make the OR conditions.
I can't see any particular elegant way to do it. So here's the straightforward approach
create function myfun (#parm1 int, #parm2 int, #andor int) returns int
begin
if (#andor = 0 AND #parm1 = 99 AND #parm2 = 99) return 1
else if (#andor = 1 AND (#parm1 = 99 OR #parm2 = 99)) return 1
return 0
end
go
select dbo.myfun(99,98,0) -- AND condition should return 0
select dbo.myfun(99,98,1) -- OR condition should return 1
select dbo.myfun(98,98,0) -- AND condition should return 0
select dbo.myfun(98,98,1) -- OR condition shoujld return 0

How to convert the result of intersection of two arrays into integer Array from a SQL query to an Integer array in postgresql

Query 1:
SELECT ARRAY(select id from contacts where id = 0)::INT[],
ARRAY[]::INT[],
ARRAY(SELECT id FROM contacts WHERE id = 0)::INT[] = ARRAY[]::int[]
Produces this result:
int4 array ?column?
{} {} TRUE
Query 2:
SELECT (ARRAY(SELECT id FROM contacts WHERE id = 0)::INT[]
& ARRAY(select id from contacts where id = 0)::INT[]),
ARRAY[]::INT[],
(ARRAY(SELECT id FROM contacts WHERE id = 0)::INT[]
& ARRAY(SELECT id FROM contacts WHERE id = 0)::INT[]) = ARRAY[]::int[]
Produces a different result:
?column? array ?column?
{} {} FALSE
Why the difference?
Is there any other way to compare an empty integer array with the result of an intersection of two arrays like in the second query?
Standard PostgreSQL does not support the ARRAY intersection operator. You must have installed the additional module intarray.
Your question boils down to this:
The intersection of two empty integer arrays yields an empty integer array. Why does this query yield false?
SELECT ('{}'::int[] & '{}'::int[]) = '{}'::int[]
Or in other syntax, meaning the same:
SELECT (ARRAY[]::int[] & ARRAY[]::int[]) = ARRAY[]::int[]
While this yields true:
SELECT '{}'::int[] = '{}'::int[]
And yes, that is a very good question.
For what it's worth, I can explain the difference:
SELECT array_dims('{}'::int[])
<NULL>
SELECT array_dims('{}'::int[] & '{}'::int[])
[1:0]
In other words, the first one is just an empty array, while the second one is a one-dimensional array with an empty element.
This can be very confusing. For instance see this thread about how to treat string_to_array() with empty output.
I am not sure the & operator does the right thing here.

Resources