SQL check for NULLs in WHERE clause (ternary operator?) - sql

What would the SQL equivalent to this C# statement be?
bool isInPast = (x != null ? x < DateTime.Now() : true)
I need to construct a WHERE clause that checks that x < NOW() only if x IS NOT NULL. x is a datetime that needs to be null sometimes, and not null other times, and I only want the WHERE clause to consider non-null values, and consider null values to be true.
Right now the clause is:
dbo.assignments.[end] < { fn NOW() }
Which works for the non-NULL cases, but NULL values always seem to make the expression evaluate to false. I tried:
dbo.assignments.[end] IS NOT NULL AND dbo.assignments.[end] < { fn NOW() }
And that seems to have no effect.

For use in a WHERE clause, you have to test separately
where dbo.assignments.[end] is null or dbo.assignments.[end] < GetDate()
or you can turn the nulls into a date (that will always be true)
where isnull(dbo.assignments.[end],0) < GetDate()
or you can do the negative test against the bit flag derived from the below
where case when dbo.assignments.[end] < GetDate() then 0 else 1 end = 1
The below is explanation and how you would derive isInPast for a SELECT clause.
bool isInPast = (x != null ? x < DateTime.Now() : true)
A bool can only have one of two results, true or false.
Looking closely at your criteria, the ONLY condition for false is when
x != null && x < now
Given that fact, it becomes an easy translation, given that in SQL, x < now can only be evaluated when x!=null, so only one condition is needed
isInPast = case when dbo.assignments.[end] < { fn NOW() } then 0 else 1 end
(1 being true and 0 being false)
Not sure what { fn NOW() } represents, but if want SQL Server to provide the current time, use either GETDATE() or if you are working with UTC data, use GETUTCDATE()
isInPast = case when dbo.assignments.[end] < GetDate() then 0 else 1 end

The one you are looking for is probably the CASE statement

You need something like
WHERE X IS NULL
OR X < NOW()

Have two separate queries, one when x is null one when is not. Trying to mix the two distinct conditions is the sure shot guaranteed way to get a bad plan. Remember that the generated plan must work for all values of x, so any optimization based on it (a range scan on an index) is no longer possible.

Related

How to add conditional statement inside SQL query?

Hi I am working on one SQL query. Below is my method name which executes query.
private string GetAggregatedOptionParametersByStyleIdCommand(string styleId, bool currentStore)
{
SELECT
opts.style, opts.option::text as {OptionKey}, opts.primary_colour, opts.secondary_colour, opts.brand_description, opts.description, params.*,
CASE WHEN {currentStore} == true THEN
FROM
rex.options opts
JOIN
rex.product_atoms atoms ON atoms.option_id = opts.option
JOIN
rex.parameters params ON atoms.id = params.product_atom_id
JOIN
rex.stores stores ON params.store = stores.id
WHERE
opts.style = '{styleId}'
}
Below is the structure of stores table.
CREATE TABLE rex.stores (
id serial NOT NULL,
close_date timestamp NOT NULL,
country text NULL,
distribution_centre text NULL,
"name" text NULL,
open_date timestamp NOT NULL,
CONSTRAINT "PK_stores" PRIMARY KEY (id)
);
So what I am trying to make is, Whenever the currentStore is true then I want to return the current store. Condition to check currentStore is
store.OpenDate <= currentDate &&
store.CloseDate >= currentDate
Whenever the currentStore is false I want to return all store. To check all store condition is
store.CloseDate >= currentDate
I am trying to add these conditions inside SQL query. I have added CASE WHEN {currentStore} == true THEN I am not sure how to add my closed store condition. Can anyone help me to complete this query? Any help would be appreciated. Thank you.
If you want to check for another possibility you can either add another WHEN or ELSE condition to your CASE statement. These need to come before the END.
For example:
CASE
WHEN x < 0 THEN 'negative'
WHEN x > 0 THEN 'positive'
ELSE 'zero'
END
I am not familiar with postgres, you may not need an END, and you may also be able to say CASE x WHEN > 0 THEN... WHEN < 0 ... meaning you only need to mention x once after CASE; again, this may not be a thing in postgres.

PostgreSQL conditional where clause

In my Ruby on Rails app I'm using blazer(https://github.com/ankane/blazer) and I have the following sql query:
SELECT *
FROM survey_results sr
LEFT JOIN clients c ON c.id = sr.client_id
WHERE sr.client_id = {client_id}
This query works really well. But I need to add conditional logic to check if client_id variable is present. If yes then I filter by this variable, if not then I not launching this where clause. How can I do it in PostgreSQL?
Check if its null OR your condition like this:
WHERE {client_id} IS NULL OR sr.client_id = {client_id}
The WHERE clause evaluate as follow: If the variable is empty, then the WHERE clause evaluate to true, and therefore - no filter. If not, it continue to the next condition of the OR
If anyone faced with the psql operator does not exist: bigint = bytea issue, here is my workaround:
WHERE ({client_id} < 0 AND sr.client_id > {client_id}) OR sr.client_id = {client_id}
Please consider that, client_id generally cannot be negative so you can use that information for eleminating the operation cast issue.
My solution:
I use spring data jpa, native query.
Here is my repository interface signature.
#Query(... where (case when 0 in :itemIds then true else i.id in :itemIds end) ...)
List<Item> getItems(#Param("itemIds) List<Long> itemIds)
Prior calling this method, I check if itemIds is null. If yes, I set value to 0L:
if(itemIds == null) {
itemIds = new ArrayList<Long>();
itemIds.add(0L);
}
itemRepo.getItems(itemIds);
My IDs starts from 1 so there is no case when ID = 0.

Can't explain this SQL query

Im sure these kind of questions are frowned upon but I really need some help. For the past 3 hours I have been staring at this SQL query and I just can't explain some of the logic in it. Normally I wouldn't ask but I'm reaching a deadline.
In the WHERE clause you will see an If construction. From what I can see it checks whether or not the Picking date was valid.
However, the way it's phrased it just looks weird, doesnt it always result in a 'Between'? (If x > y AND x < z then 'Between', else 'Not Between) = 'Between'.
And on what object does this 'if' result apply too? The way I interpret it the end result becomes WHERE 'Between' AND 'Between', which just doent make sense...
Any help is appreciated (P.S. The query is written for Access)
SELECT
DWH_PickOrderLines_Temp.*,
IIf(DWH_PickOrderLines_Temp.WayOfTransport IN ("ON", "PD"), "Kitting " & Mid(tbl_District_Activiteit.Activiteit, 8), IIf(DWH_PickOrderLines_Temp.PickMethode IN ("K", "V"), "Picking Bulk", tbl_District_Activiteit.Activiteit)) AS Activiteit,
IIf(DWH_PickOrderLines_Temp.WayOfTransport IN ("ON", "PD"), "Kitting", tbl_District_Activiteit.[Activiteit groep]) AS [Activiteit groep],
R14_Distinct_Warehouse_Location.Proces,
R14_Distinct_Warehouse_Location.Gebouw
FROM DWH_PickOrderLines_Temp
LEFT JOIN R14_Distinct_Warehouse_Location
ON DWH_PickOrderLines_Temp.PickLocation = R14_Distinct_Warehouse_Location.PickLocation
WHERE (((IIf([DWH_PickOrderLines_Temp].[PickDateTime] >= [R14_Distinct_Warehouse_Location].[tbl_Location_Zone_District.ValidFrom]
AND [DWH_PickOrderLines_Temp].[PickDateTime] < [R14_Distinct_Warehouse_Location].[tbl_Location_Zone_District.ValidTo], "Between", "Not Between")) = "Between")
AND ((IIf([DWH_PickOrderLines_Temp].[PickDateTime] >= [R14_Distinct_Warehouse_Location].[tbl_District_Activiteit.ValidFrom]
AND [DWH_PickOrderLines_Temp].[PickDateTime] < [R14_Distinct_Warehouse_Location].[tbl_District_Activiteit.ValidTo], "Between", "Not Between")) = "Between"));
the where never returns a value. and it is duplicated....
declare #pickdate int = 1
declare #validForm int = 0
print 'start'
if(((IIf(#pickdate >= #validForm AND #pickdate < #validForm, 'Between', 'Not Between')) = 'Between')
AND ((IIf(#pickdate >= #validForm AND #pickdate < #validForm, 'Between', 'Not Between')) = 'Between'))
print 'test'
I think the query miss a part (references to two tables, see my comment above). Anyway I think you can simplify where condition:
WHERE [DWH_PickOrderLines_Temp].[PickDateTime] >= [R14_Distinct_Warehouse_Location].[tbl_Location_Zone_Distric‌​t.ValidFrom]
AND [DWH_PickOrderLines_Temp].[PickDateTime] < [R14_Distinct_Warehouse_Location].[tbl_Location_Zone_Distric‌​t.ValidTo]
AND [DWH_PickOrderLines_Temp].[PickDateTime] >= [R14_Distinct_Warehouse_Location].[tbl_District_Activiteit.V‌​alidFrom]
AND [DWH_PickOrderLines_Temp].[PickDateTime] < [R14_Distinct_Warehouse_Location].[tbl_District_Activiteit.V‌​alidTo]
Why do you think if statement always results in 'Between'?
Basically, where condition boils down to:
where
pickDateTime between validFrom1 and validTo1
and
pickDateTime between validFrom2 and validTo2

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.

Alternative to relying on execution order of conditions in SQL 'where' clause

In languages such as JavaScript you can have 2 conditional statements and "protect" the second one with the first one. For instance:
if( scarryObject != null && scarryObject.scarryMethod() ) { ... }
// if scarryObject is null scarryMethod will not be called
I thought I would achieve the same in SQL like so:
where int_date > 19500101
and month(CONVERT(smalldatetime, ... int_date))
The problem here is that if int_date is some "bad" value like -1, 0, 1 the conversion will fail and the sp will stop with an error. I thought the first check int_date > 19500101 would get evaluated first and if false the second condition would be skipped.
It seems like it does not work like this... or? Is there any other way of doing this?
Thanks!
Your query is syntactically not correct, as the clausemonth(CONVERT.... is not a condition.
Let's assume you want to compare with a certain number, a possible way of expressing what you want would be
SELECT *
FROM myTable
WHERE case
when int_date > 19500101
then -1
else month(CONVERT(smalldatetime, ... int_date))
end = #YourMonth
You would 'protect' the evaluation of the 'month' and not the condition.
You could try splitting the query into two. Here is the concept:
SELECT *
FROM (
SELECT *
FROM myTable
WHERE int_date > 19500101
) t
WHERE month(CONVERT(smalldatetime, ... t.int_date))