SQL Server 2012 and NULL comparison - sql-server

Can anyone explain me why these two statements returns different results?
SELECT CASE WHEN NOT((NULL = NULL) OR (1 != 1)) THEN 1 ELSE 0 END
SELECT CASE WHEN NOT((NULL = NULL) AND (1 != 1)) THEN 1 ELSE 0 END
I know that NULL compared with anything gives false and I wanted to use that property but I stopped at commands similar to above. My real statements instead of NULLs use variables that can be NULL but I simplified them to show where is the problem. I thought that it has something with operation order but it seems that's not it.

I know that NULL compared with anything gives false
This isn't correct, NULL compared with anything evaluates to unknown, not false, a quick example:
SELECT CASE WHEN (NULL = NULL) THEN 'True'
WHEN NOT(NULL = NULL) THEN 'False'
ELSE 'Other'
END
Will give the third option of Other.
If we rewrite your logic (still the same meaning, but it becomes more clear):
SELECT CASE WHEN (NULL <> NULL) AND (1 = 1) THEN 1 ELSE 0 END
SELECT CASE WHEN (NULL <> NULL) OR (1 = 1) THEN 1 ELSE 0 END
So in the first instance you have WHEN [Unknown] AND [True] which is false, but in the second you have WHEN [Unknown] OR [True] which is true, so returns 1.
If you rewrite the query with variables, then inspect the execution plan XML, you can see that SQL Server rewrites the expression as above during compilation:
DECLARE #a INT = NULL, #b INT = NULL, #c INT = 1, #d INT = 1;
SELECT TOP 1
CASE WHEN NOT((#a = #b) OR (#c != #d)) THEN 1 ELSE 0 END,
CASE WHEN NOT((#a = #b) AND (#c != #d)) THEN 1 ELSE 0 END

-- first query
SELECT CASE WHEN NOT((NULL = NULL) AND (1 != 1)) THEN 1 ELSE 0 END
=
SELECT CASE WHEN NOT(unknown AND false) THEN 1 ELSE 0 END
=
SELECT CASE WHEN NOT(false) THEN 1 ELSE 0 END
=
SELECT CASE WHEN true THEN 1 ELSE 0 END
=
1
-- second query
SELECT CASE WHEN NOT((NULL = NULL) OR (1 != 1)) THEN 1 ELSE 0 END
=
SELECT CASE WHEN NOT(unknown OR false) THEN 1 ELSE 0 END
=
SELECT CASE WHEN NOT(unknown) THEN 1 ELSE 0 END
=
SELECT CASE WHEN unknown THEN 1 ELSE 0 END
=
else matched, so 0
And to D0dger's question from comments:
It's more interesting why SELECT CASE WHEN (NULL = NULL) OR (1 != 1) THEN 1 ELSE 0 END and SELECT CASE WHEN NOT((NULL = NULL) OR (1 != 1)) THEN 1 ELSE 0 END returns 0
SELECT CASE WHEN (NULL = NULL) OR (1 != 1) THEN 1 ELSE 0 END
=
SELECT CASE WHEN unknown OR false THEN 1 ELSE 0 END
=
SELECT CASE WHEN unknown THEN 1 ELSE 0 END
=
else matched, so 0
OR (Transact-SQL), AND (Transact-SQL)

So, there are 2 options: Either you have ANSI_NULLS ON (and you should) or you have ANSI_NULLS OFF.
In the first case, any comparison with NULL returns NULL (even comparisons between NULL values such as yours).
In the second case, sql server will evaluate comparisons between NULL values (e.g. NULL=NULL will return true).
So before considering the different results in your queries you must first consider that comparing NULL with anything, doesn't evaluate to false but NULL

Related

I'm writing a TSQL procedure and have a query that extracts data . However it's returning the divide by zero error exception

Divide by zero error exception from the below code though I have used ISNULL in my code. Any suggestions on how to fix this.
SELECT
dd.detail_id DETAIL_ID, dd.period_id PERIOD_ID, noff.FGA_ID FGA_ID,
noff.fulfillment_center FULFILLMENT_CENTER, noff.DEMAND_TYPE DEMAND_TYPE,
noff.LEGACY_SSC LEGACY_SSC,
noff.net_offset_forecast NET_OFFSET_FORECAST,
noff.forecast_to_be_consumed FORECAST_TO_BE_CONSUMED,
CASE
WHEN ISNULL(dd.DEPOT_WISE_FORECAST, 0) = 0
THEN 0
ELSE (noff.net_offset_forecast * dd.detail_depot_wise_forecast) / ISNULL((dd.agg_depot_wise_forecast), 0))
END detail_forecast_consumption,
CASE
WHEN ISNULL(dd.DEPOT_WISE_FORECAST, 0) = 0
THEN 0
ELSE (noff.FORECAST_TO_BE_CONSUMED * dd.detail_depot_wise_forecast) / ISNULL((dd.agg_depot_wise_forecast), 0))
END detail_forecast_sales
INTO
aggregate_to_detail_forecast_consumption
FROM
net_offset_forecast noff
JOIN
disaggregate_data dd ON noff.fga_id = dd.snop_item
AND noff.fulfillment_center = dd.fulfillment_center
SELECT
dd.detail_id DETAIL_ID, dd.period_id PERIOD_ID, noff.FGA_ID FGA_ID,
noff.fulfillment_center FULFILLMENT_CENTER,
noff.DEMAND_TYPE DEMAND_TYPE, noff.LEGACY_SSC LEGACY_SSC,
noff.net_offset_forecast NET_OFFSET_FORECAST,
noff.forecast_to_be_consumed FORECAST_TO_BE_CONSUMED,
CASE
WHEN ISNULL(dd.DEPOT_WISE_FORECAST, 0) = 0
THEN 0
ELSE (noff.net_offset_forecast * dd.detail_depot_wise_forecast) / ISNULL((dd.agg_depot_wise_forecast), 0))
END detail_forecast_consumption,
CASE
WHEN ISNULL(dd.DEPOT_WISE_FORECAST, 0) = 0
THEN 0
ELSE (noff.FORECAST_TO_BE_CONSUMED * dd.detail_depot_wise_forecast) / ISNULL((dd.agg_depot_wise_forecast), 0))
END detail_forecast_sales
INTO
aggregate_to_detail_forecast_consumption
FROM
net_offset_forecast noff
JOIN
disaggregate_data dd ON noff.fga_id = dd.snop_item
AND noff.fulfillment_center = dd.fulfillment_center
There should be No divide by zero exception with the above code
You don't want / ISNULL(dd.agg_depot_wise_forecast, 0).
This will return either dd.agg_depot_wise_forecast or 0, depending on whether the first argument is NULL.
You want to use NULLIF(): / NULLIF(dd.agg_depot_wise_forecast, 0).
This will replace zero values with NULL, avoiding the error.
You should check the value and do not attempt top divide when it is NULL. When it is NULL, you are replacing it with 0 and causing a divide by zero error.
Here is a sample:
declare #sample table (id int identity, v1 decimal(10,2), v2 decimal(10,2) null);
insert into #sample (v1, v2) values
(10,2),
(10,null);
--select *, v1/isnull(v2,0)
--from #sample;
select *, case when v2 is null then 0 else v1/v2 end as MyVal
from #sample;
Commented part is how you do it and would error.
Kindly replace your case statement with below corrected case statement.
Changes:
ISNULL((dd.agg_depot_wise_forecast),0) -> ISNULL(NULLIF(dd.agg_depot_wise_forecast,0), 1)
NULLIF() will replace 0 value with NULL value, so ISNULL() will always take 1 from second argument.
CASE WHEN ISNULL(dd.DEPOT_WISE_FORECAST, 0) = 0 THEN 0 ELSE (noff.net_offset_forecast * dd.detail_depot_wise_forecast) / ISNULL(NULLIF(dd.agg_depot_wise_forecast,0), 1)) END detail_forecast_consumption, CASE WHEN ISNULL(dd.DEPOT_WISE_FORECAST, 0) = 0 THEN 0 ELSE (noff.FORECAST_TO_BE_CONSUMED * dd.detail_depot_wise_forecast) / ISNULL(NULLIF(dd.agg_depot_wise_forecast,0), 1)) END detail_forecast_sales

how to write an expression in SSRS to filter data

I need to filter my result based on Parameter value. If it says "Open" then DueDate column should be = NULL, if says 'Closed' that DueDate = NOT NULL, and if it says "Both" then it should grab all DueDates
I have created query parameter "Status" that gives me 3 possible values:
Next I created report parameter "Status" and Allow Multiple Values
Now I go to my main query and go to Filters:
And here I cant understand how can I write an expression saying:
If report "status" value = "Open" then show me the result where DueDate IS NULL,
If report "Status" value = "Closed" then DueDate IS NOT NULL ,
And If report "Status" value = "Both" then show me all DueDates ( null and not null)
Another thing is I already have case statement in my query:
ALTER Procedure
AS
#ShowOpen bit = 0,
#ShowClosed bit = 0
SELECT
FROM
WHERE
AND
(
(CASE WHEN (#ShowOpen = 1) THEN
CASE WHEN (tblNoteRecipients.CompletedDate IS NULL and tblNoteRecipients.IsDiary = 1) or tblNoteRecipients.UserGUID is null THEN 1 ELSE 0 END
-- CASE WHEN (tblNoteRecipients.CompletedDate IS NULL) THEN 1 ELSE 0 END
ELSE
1
END = 1)
AND
(CASE WHEN (#ShowClosed = 1) THEN
CASE WHEN (tblNoteRecipients.CompletedDate IS NULL) THEN 0 ELSE 1 END
ELSE
1
END = 1)
OR ((#ShowOpen = 1) AND (#ShowClosed = 1))
)
Is any way out of it in SSRS I can create parameter that accepts values Open, Closed and Both?
Added test1
Add a Filter in the tablix like this:
In Expression use:
=IIF(
(Array.IndexOf(Parameters!Status.Value,"Open")>-1 and
Isnothing(Fields!DueDate.Value)) or
(Array.IndexOf(Parameters!Status.Value,"Closed")>-1 and not
Isnothing(Fields!DueDate.Value)) or
(Array.IndexOf(Parameters!Status.Value,"Both")>-1),
"Include","Exclude")
For Value use:
="Include"

IIF to Case statement

I have the below code in Access which I'm migrating to SQL Server,
IIf([code] Is Not Null,IIf([code]<>'ABC',
IIf([length] Is Null,1,IIf([Length]=0,1,
IIf([width] Is Null,1,IIf([Width]=0,1,
IIf([height] Is Null,1,IIf([Height]=0,1,
0))))))))
I believe it checks for code not null and not equal to 'ABC' then length should be null or 0 then it assigns a value 1 of the condition fails then it is 0. I need a little help with writing it with case.
Literal translating:
CASE WHEN [code] Is Not Null
THEN
CASE WHEN [code]<>'ABC'
THEN
CASE WHEN [length] Is Null
THEN 1
ELSE
CASE WHEN [Length]=0
THEN 1
ELSE
CASE WHEN [width] Is Null
THEN 1
ELSE
CASE WHEN [Width]=0
THEN 1
ELSE
CASE WHEN [height] Is Null
THEN 1
ELSE
CASE WHEN [Height]=0
THEN 1
ELSE 0
END
END
END
END
END
END
END
END
Simplified in :
CASE WHEN ISNULL([code], 'ABC') <>'ABC' AND
(ISNULL([length], 0) = 0 OR
ISNULL([width], 0) = 0 OR
ISNULL([height], 0) = 0)
THEN 1
ELSE 0
END
IIf in JET SQL translates to CASE WHEN in SQL Server as follows:
IIf(condition, whenTrue, whenFalse)
translates directly to
CASE WHEN condition THEN whenTrue ELSE whenFalse END
For nested expressions
IIf(condition1, whenTrue1, IIf(condition2, whenTrue2, whenFalse))
you can either translate them directly:
CASE WHEN condition1
THEN whenTrue1
ELSE CASE WHEN condition2
THEN whenTrue2
ELSE whenFalse
END
END
or use the fact that CASE allows you to specify multiple WHEN conditions:
CASE WHEN condition1 THEN whenTrue1
WHEN condition2 THEN whenTrue2
ELSE whenFalse
END
With this knowledge, translating your statement should be easy and is left as an exercise to the reader (I don't want to spoil the fun).

'an expression of non boolean type specified' in CASE WHEN

Given this T-SQL code:
SELECT CASE
WHEN ( Cast (CASE
WHEN Isnull(bpe.new, 0) = 0
AND Isnull(bpe.regular, 0) = 0
AND Isnull(bpe.bargain, 0) = 0 THEN 0
ELSE 1
END AS BIT)
AND #siteID IS NULL
OR bpe.siteid = #siteID ) THEN 1
ELSE 0
END AS SiteHasBrandException
FROM brandpromoexceptions AS bpe
I get the error message:
an expression of non boolean type specified in the context where a condition is expected TSQL
Why is that?
If you mean zero as "false" for that first bit, the query should be equivalent to:
SELECT CASE WHEN
NOT ( ISNULL(bpe.new, 0) = 0
AND Isnull(bpe.regular, 0) = 0
AND Isnull(bpe.bargain, 0) = 0 )
AND ( #siteID IS NULL
OR bpe.siteid = #siteID )
THEN 1
ELSE 0
END AS SiteHasBrandException
FROM brandpromoexceptions AS bpe
You may need to be careful with that "OR" clause, or add parentheses for clarification. I guessed here.
You are passing your first CASE statement a call to CAST rather than a conditional. The error message pretty much spells it out.
I'm not sure exactly what you are trying to do, but you seem to understand that you need to pass a boolean to the CASE WHEN based on your subsequent use of CASE so hopefully this helps.
this is a shot in the dark by try changing your code to this:
SELECT CASE
WHEN ( Cast (CASE
WHEN Isnull(bpe.new, 0) = 0
AND Isnull(bpe.regular, 0) = 0
AND Isnull(bpe.bargain, 0) = 0 THEN 0
ELSE 1
END AS BIT) = 1
AND #siteID IS NULL
OR bpe.siteid = #siteID ) THEN 1
ELSE 0
END AS SiteHasBrandException
FROM brandpromoexceptions AS bpe

SELECT IF into custom variable

I have this:
SELECT
#is_daily_rollup = CASE WHEN rt.[id]=1 THEN 1 ELSE 0,
#is_weekly_rollup = CASE WHEN rt.[id]=2 THEN 1 ELSE 0
But sql server is complaining about syntax. How would I go about implementing this conditional value into a variable?
For a CASE statement you need to provide an END
CASE WHEN rt.[id]=1 THEN 1 ELSE 0 END
CASE WHEN rt.[id]=2 THEN 1 ELSE 0 END
so your full query would be:
SELECT #is_daily_rollup = CASE WHEN rt.[id]=1 THEN 1 ELSE 0 END,
#is_weekly_rollup = CASE WHEN rt.[id]=2 THEN 1 ELSE 0 END
You are missing the END for your CASE:
SELECT
#is_daily_rollup = CASE WHEN rt.[id]=1 THEN 1 ELSE 0 END,
#is_weekly_rollup = CASE WHEN rt.[id]=2 THEN 1 ELSE 0 END
Of course, this is assuming that you already declared yor variables.

Resources