How to check if a string is IN a text[]? - sql

This is the code I am running in postgres:
SELECT CASE WHEN 'oz' != ANY(('{oz,l,m,g}')::text[]) THEN 'HELOO' ELSE 'WOAH' END;
AFAIK, this should select 'WOAH' but it gives 'HELOO'. Now, with any string on the left side, it always selects 'HELOO' (I have tried 'monkey', and 'mg').
When I run the same code but now without a !:
SELECT CASE WHEN 'oz' = ANY(('{oz,l,m,g}')::text[]) THEN 'HELOO' ELSE 'WOAH' END;
It works as intended i.e. it selects 'HELOO' and when the left side string is 'monkey', it selects 'WOAH'.
How can I check here if the left side string is NOT in the right side text[]?

From the documentation of ANY:
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 when you say 'oz' != ANY(('{oz,l,m,g}')::text[]), Postgres examines in turn 'oz' != 'oz', 'oz' != 'l', 'oz' != 'm', and 'oz' != 'g'. Although the first of those is false, the others are true, so the ANY expression is true.
What you want instead is ALL, where:
The result is “false” if any false result is found.
So when you say 'oz' != ALL(('{oz,l,m,g}')::text[]), Postgres examines the same set of comparisons, but because the first one is false, the whole expression is false.
Effectively, ANY is using an OR between the possibilities, and ALL is using an AND.
This explains why NOT ('oz' = ANY(('{oz,l}')::text[])) also works: it is equivalent to NOT ('oz' = 'oz' OR 'oz' = 'l') and De Morgan's laws tell us that's the same as (NOT 'oz' = 'oz') AND (NOT 'oz' = 'l'); that is ('oz' != 'oz') AND ('oz' != 'l'), which is what we wanted.

You want <> ALL:
SELECT (CASE WHEN 'oz' <> ALL(('{oz,l,m,g}')::text[])
THEN 'HELOO' ELSE 'WOAH'
END);
Note that the important part is ALL; != also works but <> is the traditional SQL operator for not-equals.
I should note that you can also use:
SELECT (CASE WHEN NOT ('oz' = ANY(('{oz,l,m,g}')::text[]))
THEN 'HELOO' ELSE 'WOAH'
END);

demo:db<>fiddle
You can either use the construction: 'oz' = ANY('{...}') IS NOT TRUE
SELECT
CASE
WHEN 'oz' = ANY(('{oz,l,m,g}')::text[]) IS NOT TRUE THEN 'HELOO'
ELSE 'WOAH'
END;
or use a completely different quantifier function: ALL as #GordonLinoff said

Related

how to prevent converting the text into boolean by the use of when statement in postgresql?

select fti.pa_serial_,fti.homeownerm_name,fti.ward_,fti.villagetole,fti.status,
ftrq.date_reporting, ftrq.name_of_recorder_reporting,
case
when fti.status='terminate' then ftrq.is_the_site_cleared ='1' end as is_the_site_cleared from fti join ftrq on ftrq.fulcrum_parent_id = fti.fulcrum_id
Here, is_the_site_cleared is text type of column which is converted into boolean by the when statement written and hence does not print as 1 and takes as true. I explicitly used print '1'. But this also did not work. My aim is to display '1' in the column 'is_the_site_cleared' when the value of fti.status='terminate'. Please help!!!
How about using integers rather than booleans?
select fti.pa_serial_, fti.homeownerm_name, fti.ward_,
fti.villagetole, fti.status, ftrq.date_reporting,
ftrq.name_of_recorder_reporting,
(case when fti.status = 'terminate' -- and ftrq.is_the_site_cleared = '1'
then 1 else 0
end) as is_the_site_cleared
from fti join
ftrq
on ftrq.fulcrum_parent_id = fti.fulcrum_id ;
From the description, I cannot tell if you want to include the condition ftrq.is_the_site_cleared = '1' in the when condition. But the idea is to have the then and else return numbers if that is what you want to see.

Decode with AND clause oracle

I want to use parameter in my query but I can't handle with it
I have 3 big selects to raport and I just want to use parameter for some part of code which depends from choice
I have 3 different Where conditions
1st
..WHERE A.CANCELLED = 'FALSE' AND a.open_amount!=0 AND A.IDENTITY = '&client_id'..
2nd
...WHERE A.CANCELLED = 'FALSE' AND A.IDENTITY = '&client_id' ...
3rd
WHERE AND A.CANCELLED = 'FALSE' AND a.invoice_amount != a.open_amount AND A.IDENTITY = '&client_id'
I tried with decode but I guess it could be ok if there would be value in 2nd case but there isn't and I cant decode like this
WHERE decode(xxx,x1,'AND a.open_amount!= 0',x2,'',x3, 'AND a.invoice_amount != a.open_amount')
How I should solve that problem any tips?
Do you mean, if the first "where condition" OR the second OR the third is/are TRUE, you want the overall to be TRUE (select the row), and you are looking for a simplified way to write it? That is, without simply combining them with OR?
To achieve that, you don't need CASE and nested CASE statements or DECODE. You could do it like this:
WHERE A.CANCELLED = 'FALSE'
AND A.IDENTITY = '&client_id'
AND ( (xxx = x1 AND a.open_amount != 0) OR (xxx = x2) OR
(xxx = x3 AND a.invoice_amount != a.open_amount) )
This is more readable, the intent is clear, it will be easier to modify if needed, ...
You can try something like -
WHERE A.CANCELLED = 'FALSE'
AND A.IDENTITY = '&client_id'
AND a.open_amount <>
(CASE
WHEN x1 THEN 0
WHEN x2 THEN a.open_amount + 1 -- This needs to be something that is always TRUE, to nullify the condition
WHEN x3 THEN a.invoice_amount
END);
Edit: This is based on the assumption that a.open_amount is a NUMBER and uses a quick hack where we create an always TRUE condition like x <> x + 1. You should probably change this to whatever suits you better based on your data.

change where condition based on column value

I faced the following requirement. The following query is called by a procedure. The value p_pac_code is the input parameter of the procedure.
The requirement is the query should have an additional condition sp_sbsp.SUBSPCLTY_CODE!='C430 if the p_pac_code value is '008'.
For any other p_pac_code value, it should run as it is below. Is there way to do this by adding an additional condition in the WHERE clause?
As for now, I have done this using IF.....ELSE using the query two times separately depending on p_pac_code value. But I am required to find a way to do with just adding a condition to this single query.
SELECT ptg.group_cid
FROM PRVDR_TYPE_X_SPCLTY_SUBSPCLTY ptxss,
PT_X_SP_SSP_STATUS pxsst ,
pt_sp_ssp_x_group ptg,
group_x_group_store gg,
specialty_subspecialty sp_sbsp,
treatment_type tt,
provider_type pt
WHERE
pt.PRVDR_TYPE_CODE = ptxss.PRVDR_TYPE_CODE
AND tt.TRTMNT_TYPE_CODE = pxsst.TRTMNT_TYPE_CODE
AND ptxss.PRVDR_TYPE_X_SPCLTY_SID = pxsst.PRVDR_TYPE_X_SPCLTY_SID
AND tt.TRTMNT_TYPE_CODE = p_pac_code
AND TRUNC(SYSDATE) BETWEEN TRUNC(PXSST.FROM_DATE) AND TRUNC(PXSST.TO_DATE)
AND ptg.prvdr_type_code =ptxss.prvdr_type_code
AND ptg.spclty_subspclty_sid = ptxss.spclty_subspclty_sid
AND ptxss.spclty_subspclty_sid = sp_sbsp.spclty_subspclty_sid
AND ptg.spclty_subspclty_sid = sp_sbsp.spclty_subspclty_sid
AND ptg.status_cid = 2
AND ptg.group_cid = gg.group_cid
AND gg.group_store_cid = 16
AND gg.status_cid = 2;
Thanks in advance.
You can simply add a condition like this:
... and (
( sp_sbsp.SUBSPCLTY_CODE!='C430' and p_pac_code = '008')
OR
NVL(p_pac_code, '-') != '008'
)
This can be re-written in different ways, this one is quite self-explanatory
Just add:
AND NOT ( NVL( sp_sbsp.SUBSPCLTY_CODE, 'x' ) = 'C430'
AND NVL( p_pac_code value, 'x' ) = '008' )
to the where clause.
The NVL function is used so that it will match NULL values (if they exist in your data); otherwise, even though NULL does not match C430 you will still find that NULL = 'C430' and NULL <> 'C430' and NOT( NULL = 'C430' ) will all return false.
Quite easy. Add the following condition:
AND (sp_sbsp.SUBSPCLTY_CODE != 'C430' OR p_pac_code value != '008')
(don't forget the parenthesis)
Just sharing
SELECT ptg.group_cid
FROM PRVDR_TYPE_X_SPCLTY_SUBSPCLTY ptxss
WHERE
pt.PRVDR_TYPE_CODE = ptxss.PRVDR_TYPE_CODE
&&((tt.TRTMNT_TYPE_CODE = pxsst.TRTMNT_TYPE_CODE)
or (tt.TRTMNT_TYPE_CODE = pxsst.TRTMNT_TYPE_CODE))
just use the parenthesis to specify where the conditionmust implement.

SQL: field = other_field returns false even if they are identical (NULL values)

I have a difficulty because when comparing two fields in a subquery, although the fields are identical i.e. they both have NULL values, the comparison returns a FALSE result
Therfore NULL = NULL is returning FALSE
Now I know that NULLs are supposed to be compared with the IS operator, however when I compare two fields how am I supposed to know they contain a null? I need to compare two fields for identical data both if the values are NULL or not.
Consider this SQL:
SELECT
*
FROM
fts.fts_customers_data_50360001
WHERE
fts.fts_customers_data_50360001.record_type = 15
AND
fts.fts_customers_data_50360001.mid = 103650360001
AND NOT EXISTS
(
SELECT
fts.temp_fees_50360001.record_type
FROM
fts.temp_fees_50360001
WHERE
fts.temp_fees_50360001.record_type = fts.fts_customers_data_50360001.record_type
AND
fts.temp_fees_50360001.merch_id = fts.fts_customers_data_50360001.mid
AND
fts.temp_fees_50360001.fee_curr = fts.fts_customers_data_50360001.currency
AND
fts.temp_fees_50360001.card_scheme = fts.fts_customers_data_50360001.card_scheme
AND
fts.temp_fees_50360001.tran_type = fts.fts_customers_data_50360001.fee_type
AND
fts.temp_fees_50360001.area = fts.fts_customers_data_50360001.region
AND
fts.temp_fees_50360001.srvc_type = fts.fts_customers_data_50360001.card_type
);
In the query above,
fts.temp_fees_50360001.card_scheme = fts.fts_customers_data_50360001.card_scheme
both have NULL values inside but the comparison returns false .. too bad
ANY IDEAS WOULD BE MUCH APPRECIATED
As the others have pointed out, NULL cannot be compared with NULL.
In Postgres you can shorten your expressions by using the operator IS DISTINCT FROM which is a null-safe replacement for <>. In your case you'd need to use IS NOT DISTINCT FROM to compare for equality (looks a bit the wrong way round but unfortunately there is no corresponding IS EQUAL TO defined in the SQL standard).
From the manual:
Ordinary comparison operators yield null (signifying "unknown"), not true or false, when either input is null. For example, 7 = NULL yields null, as does 7 <> NULL. When this behavior is not suitable, use the IS [ NOT ] DISTINCT FROM constructs:
So, instead of
(fts.temp_fees_50360001.record_type = fts.fts_customers_data_50360001.record_type
OR (fts.temp_fees_50360001.record_type IS NULL
AND fts.fts_customers_data_50360001.record_type IS NULL)
)
you can use:
(fts.temp_fees_50360001.record_type IS NOT DISTINCT FROM fts.fts_customers_data_50360001.record_type)
to handle NULL values automatically. The condition looks a bit strange if you want to compare for equality but it still is quite short.
First of all, use aliases for your tables, your query will be MUCH more readable:
select *
from fts.fts_customers_data_50360001 as d
where
d.record_type = 15 and
d.mid = 103650360001 and
not exists
(
select *
from fts.temp_fees_50360001 as f
where
f.record_type = d.record_type and
f.merch_id = d.mid and
f.fee_curr = d.currency and
f.card_scheme = d.card_scheme and
f.tran_type = d.fee_type and
f.area = d.region and
f.srvc_type = d.card_type
)
As for your question, there's several ways to do this, for example, you can use syntax like this:
...
(
f.card_scheme is null and d.card_scheme is null or
f.card_scheme = d.card_scheme
)
...
Or use coalesce with some value that couldn't be stored in your column:
...
coalesce(f.card_scheme, -1) = coalesce(d.card_scheme, -1)
...
Recently I also like using exists with intersect for this type of comparisons:
...
exists (select f.card_scheme, f.tran_type intersect select d.card_scheme, d.tran_type)
...
Just a side note - you have to be careful when writing queries like this and check query plans to be sure your indexes are used.
In SQL, null is never equal to null. The only way to get a true result for a comparison with null is via the special tests:
IS NULL
IS NOT NULL
In your case, you must cater specifically for the "two nulls" case being considered equal:
AND (fts.temp_fees_50360001.card_scheme = fts.fts_customers_data_50360001.card_scheme
OR (fts.temp_fees_50360001.card_scheme IS NULL
AND fts.fts_customers_data_50360001.card_scheme IS NULL)
)
There's no getting around dealing with it (although there are a few variations).
The following inner SELECT works (but I give no guarantee regarding performance):
SELECT
fts.temp_fees_50360001.record_type
FROM
fts.temp_fees_50360001
WHERE
(fts.temp_fees_50360001.record_type = fts.fts_customers_data_50360001.record_type
OR (fts.temp_fees_50360001.record_type IS NULL AND fts.fts_customers_data_50360001.record_type IS NULL))
AND
(fts.temp_fees_50360001.merch_id = fts.fts_customers_data_50360001.mid
OR (fts.temp_fees_50360001.merch_id IS NULL AND fts.fts_customers_data_50360001.mid IS NULL))
AND
(fts.temp_fees_50360001.fee_curr = fts.fts_customers_data_50360001.currency
OR (fts.temp_fees_50360001.fee_curr IS NULL AND fts.fts_customers_data_50360001.currency IS NULL))
AND
(fts.temp_fees_50360001.card_scheme = fts.fts_customers_data_50360001.card_scheme
OR (fts.temp_fees_50360001.card_scheme IS NULL AND fts.fts_customers_data_50360001.card_scheme IS NULL))
AND
(fts.temp_fees_50360001.tran_type = fts.fts_customers_data_50360001.fee_type
OR (fts.temp_fees_50360001.tran_type IS NULL AND fts.fts_customers_data_50360001.fee_type IS NULL))
AND
(fts.temp_fees_50360001.area = fts.fts_customers_data_50360001.region
OR (fts.temp_fees_50360001.area IS NULL AND fts.fts_customers_data_50360001.region IS NULL))
AND
(fts.temp_fees_50360001.srvc_type = fts.fts_customers_data_50360001.card_type
OR (fts.temp_fees_50360001.srvc_type IS NULL AND fts.fts_customers_data_50360001.card_type))

SQL - Trouble with simple select query

I'm having some trouble figuring out why the SQL query below isn't working ... when members_only is set to 0, it's still showing them...
SELECT *
FROM reports
WHERE story = "1"
OR barebones = "1"
AND members_only = "1"
It depends on your data, but you may not be aware that AND has higher precedence in SQL than OR, so your query really evaluates to this:
SELECT *
FROM reports
WHERE story = '1'
OR (barebones = '1' AND members_only = '1')
Consider using different brackets per the other answers to explicitly declare your intentions
Use brackets to distinguish your clarify the WHERE-condition.
SELECT *
FROM reports
WHERE (story = '1' OR barebones = '1')
AND members_only = '1'
I would say because it reads the query as:
WHERE (story = '1') OR (barebones = '1' AND members_only = '1')
since story = '1', the condition is satisfied
OR clauses can be tricky - you often need to explicitly tell the query where it belongs. I assume, you want this:
WHERE (story = '1' OR barebones = '1') AND members_only = '1'
Missing parenthesis?
Did you want to do something like this:
SELECT *
FROM reports
WHERE (story = "1" OR barebones = "1") AND members_only = "1"
You are missing parenthesis.
The following code will work, assuming members_only HAS to be "1", but only story or barebones has to be "1".
SELECT *
FROM reports
WHERE
(story = "1" OR barebones = "1")
AND members_only = "1"
You should read up on TSQL Operator Precedence
In your original code, assume the following:
store = 1
barebones = 1
*members_only* = 0
Due to Operator Precedence, barebones and *members_only* is evaluated first, and evaluates to false.
Following this, the result of the first boolean evaluation (false) (also known as The Right Side) is compared to (story = "1") (also known as The Left Side).
The Right Side evaluates to false, but The Left Side evaluates to true.
Since the final boolean compression uses the OR operator, the end result is a TRUE, thus that record is indeed returned, no matter the value of either barebones or *members_only*, since The Left Side always evaluates to True.