Nested If Statement Using Case Statement in SQL Server - sql-server

I inherited a spreadsheet from a co-worker who used a nested If statement as shown below:
=IF(AND([#[Pred Type]]="PR_FS",[#[ST Finish]]="A"),
0,
IF(AND([#[Pred Type]]="PR_SS", OR([#[ACT_START_DATE]]<>0,[#[ST Start]]="A")),
0,
IF(AND([#[Pred Type]]="PR_SF",OR([#[ACT_START_DATE]]<>0,[#[ST Start]]="A")),
0,
IF(AND([#[Pred Type]]="PR_FF",[#[STFinish]]="A"),0,1)
)
)
)
I know I can write a CASE statement that would mimic this but can't figure out the syntax for it. I tried:
,CASE WHEN #Preds.Pred_Type = 'PR_FS' and #Tbl_Sched_Act.Expected_Finish = 'A' THEN 0
WHEN #Preds.Pred_Type = 'PR_SS' OR ACT_START_DATE IS NOT NULL OR Start_RAW IS NOT NULL THEN 0
WHEN #Preds.Pred_Type = 'PR_SF'OR ACT_START_DATE IS NOT NULL OR Start_RAW IS NOT NULL THEN 0
WHEN #Preds.Pred_Type = 'PR_FF' and #Tbl_Sched_Act.Expected_Finish = 'A' THEN 0
ELSE 1
END as PRED_MET
However, the CASE statement does not evaluate any of the items to be equal to 1. They all evaluate to 0 which isn't correct.
Does anyone have any idea on how I could write my CASE statement correctly?

You need to group the OR inside parenthesis () to force operator precedence.
CASE
WHEN #Preds.Pred_Type = 'PR_FS' and #Tbl_Sched_Act.Expected_Finish = 'A'
THEN 0
WHEN #Preds.Pred_Type = 'PR_SS'
AND (ACT_START_DATE <> 0 OR Start_RAW <> 0)
THEN 0
WHEN #Preds.Pred_Type = 'PR_SF'
AND (ACT_START_DATE <> 0 OR Start_RAW <> 0)
THEN 0
WHEN #Preds.Pred_Type = 'PR_FF' and #Tbl_Sched_Act.Expected_Finish = 'A'
THEN 0
ELSE 1
END as PRED_MET
NOTE:: If a number is <> 0 then also is NOT NULL DEMO
But that can also be simplify to:
CASE
WHEN #Tbl_Sched_Act.Expected_Finish = 'A'
AND #Preds.Pred_Type IN ('PR_FS', 'PR_FF')
THEN 0
WHEN (ACT_START_DATE <> 0 OR Start_RAW <> 0)
AND (#Preds.Pred_Type IN ('PR_SS','PR_SF')
THEN 0
ELSE 1
END as PRED_MET

Related

What causing errors with this stored procedure?

I have a stored procedure to process monthly jobs. The stored procedure will loop through all the available jobs and do some calculation before it's inserted into a table. It was fine for most jobs but for some jobs, there will be an error
Subquery returned more than 1 value. This is not permitted when the
subquery follows =, !=, <, <= , >, >= or when the subquery is used as
an expression.
Below is the section where it says the error occurred:
IF EXISTS( SELECT *
FROM aastocks aastk
WHERE aastk.estno = #lcEstno
and 0 in (aastk.cartonratio1,
aastk.cartonratio2,
aastk.cartonratio3,
aastk.cartonratio4,
aastk.cartonratio5)
) EXECUTE ru_est_stocksum #lcEstNo
#lcEstNo was defined in the previous code.
SET #lcEstno = (SELECT MAX(estno) FROM dbo.v_est_joblist WHERE ljob = #tcLjob)
MAX() should return only 1 value and I don't understand why the multi value error.
Well, as it turns out, the line number of the error is not the same as the line number in the Stored Procedure. So the problem actually resides in the following code(After I comment out the below section and there is no error). But I am not able to find out which subquery is returning more than 1 value. Any idea ?
INSERT INTO #JCDTotals
SELECT #tcLjob, coalesce(cast( ap.deptno as char(4)),'????') as deptno,
CASE WHEN coalesce(v_depuse_qty.DepUse, 1) = 1 THEN
CASE WHEN #tcCostBase = 'D' THEN CASE WHEN ap.procgroup = 'I' THEN aaprocqt.dmcstmat1 + aaprocqt.dmcstmat2 + aaprocqt.dmcstmat3 END
WHEN #tcCostBase = 'A' THEN CASE WHEN ap.procgroup = 'I' THEN aaprocqt.costmats1 + aaprocqt.costmats2 + aaprocqt.costmats3 END
ELSE ' 0 '
END
ELSE 0 END AS ecostmats,
CASE WHEN coalesce(v_depuse_qty.DepUse, 1) = 1 THEN CASE WHEN #tcCostBase = 'D' THEN (1 / (1 + (ap.burden/100))) ELSE 1 END
* aaprocqt.costlab
ELSE 0
END AS ecostlab,
CASE WHEN coalesce(v_depuse_qty.DepUse, 1) = 1 THEN aaprocqt.proctime
ELSE 0
END AS eprochours,
0.0 AS bcostmats,
0.0 AS bcostlab,
0 AS bprochours,
0.0 AS acostmats,
0.0 AS acostlab,
0 AS aprochours,
0.0 AS chgcost,
0 AS dirlab,
0.0 AS aacost,
0.0 AS pecost,
0.0 AS otcost,
0 AS sell
FROM (
SELECT aaproces.idnumber,
aaproces.estno,
aaproces.procgroup,
aaproces.partno,
aaproces.[lineno],
aaproces.counter,
ssproces.burden,
case WHEN aaproces.procgroup = 'I' then ssinvent.deptno
else ssproces.deptno end as deptno,
case WHEN aaproces.procgroup = 'I' then ssinvent.matno
else STR(ssproces.procno,6) end as procno,
case WHEN aaproces.procgroup = 'I' then ssinvent.material
else ssproces.process end as process
FROM dbo.aaproces
LEFT JOIN dbo.SSINVENT ON ssinvent.matno = aaproces.matno
AND ssinvent.invtype IN ('G', 'I', 'F', 'S')
LEFT JOIN dbo.SSPROCES ON ssproces.procno = aaproces.procno
WHERE ((aaproces.procgroup = 'I' and ssinvent.deptno is not null)
OR (aaproces.procgroup <> 'I' and ssproces.deptno is not null))
AND ((aaproces.procgroup = 'I' and ssinvent.matno is not null)
OR (aaproces.procgroup <> 'I' and ssproces.procno is not null))
) ap
INNER join dbo.v_est_joblist
on v_est_joblist.estno = ap.estno
LEFT OUTER JOIN dbo.aaprocqt
on aaprocqt.estno = ap.estno
and ap.[lineno] = aaprocqt.[lineno]and aaprocqt.procgroup = ap.procgroup
and ap.partno = aaprocqt.partno and ap.counter = aaprocqt.counter and aaprocqt.qty = v_est_joblist.alt
LEFT OUTER JOIN dbo.v_depuse_qty
ON v_est_joblist.estno = v_depuse_qty.estno
AND v_depuse_qty.deptno = ap.deptno
AND V_DEPUSE_QTY.LJOB = V_EST_JOBLIST.LJOB
AND V_DEPUSE_QTY.ALT = V_EST_JOBLIST.ALT
WHERE v_est_joblist.ljob = #tcLjob
-- AND ssdept.divno like #lcDivNo
--------------------------------------------------------------------------- PROCESS MATERIAL1,MATERIAL2,MATERIAL3 (ESTIMATED)
UNION ALL
SELECT #tcLjob, coalesce(ssinvent.deptno,'????') as deptno,
CASE WHEN coalesce((SELECT v_depuse_qty.DepUse FROM dbo.v_depuse_qty WHERE v_est_joblist.estno = v_depuse_qty.estno
AND ssinvent.deptno = v_depuse_qty.deptno
AND V_DEPUSE_QTY.LJOB = V_EST_JOBLIST.LJOB
AND V_DEPUSE_QTY.ALT = V_EST_JOBLIST.ALT), 1) = 1 THEN CASE WHEN sq.CopyNum = 1 THEN CASE WHEN #tcCostBase = 'D' THEN aaprocqt.dmcstmat1 ELSE aaprocqt.costmats1 END
WHEN sq.CopyNum = 2 THEN CASE WHEN #tcCostBase = 'D' THEN aaprocqt.dmcstmat2 ELSE aaprocqt.costmats2 END
WHEN sq.CopyNum = 3 THEN CASE WHEN #tcCostBase = 'D' THEN aaprocqt.dmcstmat3 ELSE aaprocqt.costmats3 END
END
ELSE 0
END AS ecostmats,
0 AS ecostlab,
0 AS eprochours,
0 AS bcostmats,
0 AS bcostlab,
0 AS bprochours,
0.0 AS acostmats,
0.0 AS acostlab,
0 AS aprochours,
0.0 AS chgcost,
0 AS dirlab,
0.0 AS aacost,
0.0 AS pecost,
0.0 AS otcost,
0 AS sell
FROM dbo.aaproces JOIN dbo.v_est_joblist on v_est_joblist.estno = aaproces.estno
CROSS JOIN #NumCopies sq
LEFT JOIN dbo.ssproces on aaproces.procno = ssproces.procno
LEFT JOIN dbo.ssinvent on ssinvent.matno = ( case when sq.CopyNum = 1 then CASE WHEN aaproces.manmatno = '' THEN ssproces.matno1 ELSE aaproces.manmatno END
when sq.CopyNum = 2 then CASE WHEN aaproces.manmatno2 = '' THEN ssproces.matno2 ELSE aaproces.manmatno2 END
when sq.CopyNum = 3 then CASE WHEN aaproces.manmatno3 = '' THEN ssproces.matno3 ELSE aaproces.manmatno3 END
end)
JOIN dbo.aaprocqt on aaprocqt.estno = aaproces.estno and aaprocqt.procgroup = aaproces.procgroup
and aaproces.partno = aaprocqt.partno and aaproces.[lineno] = aaprocqt.[lineno]
and aaproces.counter = aaprocqt.counter and aaprocqt.qty = v_est_joblist.alt
WHERE aaproces.procgroup <> 'I'
--and sq.CopyNum < 4
and case when sq.CopyNum = 1 then (manmatno + matno1 )
when sq.CopyNum = 2 then (aaproces.manmatno2 + ssproces.matno2 )
when sq.CopyNum = 3 then (manmatno3 + matno3 )
end <> ''
AND v_est_joblist.ljob = #tcLjob
--------------------------------------------------------------------------- STOCKS (ESTIMATED)
UNION ALL
SELECT #tcLjob, coalesce(case when aastocks.houvend = 'H' then ssinvent.deptno
else case when aapthead.parttype = 'S' then #opt_stockccs
else #opt_stockccr end end,'????')
as deptno,
CASE WHEN coalesce((SELECT v_depuse_qty.DepUse FROM dbo.v_depuse_qty WHERE v_est_joblist.estno = v_depuse_qty.estno
AND v_depuse_qty.deptno = ssdept.deptno
AND V_DEPUSE_QTY.LJOB = V_EST_JOBLIST.LJOB
AND V_DEPUSE_QTY.ALT = V_EST_JOBLIST.ALT), 1) = 1 THEN CASE WHEN #tcCostBase = 'D' THEN
CASE WHEN aastocks.houvend = 'V' THEN (1 / (1 + (ssdept.burden/100)))
ELSE (1 / (1 + (ssinvent.burden/100))) END
ELSE 1 END *
case when v_est_joblist.alt = 1 then aastocks.stkcost1 * aastocks.cartonratio1
when v_est_joblist.alt = 2 then aastocks.stkcost2 * aastocks.cartonratio2
when v_est_joblist.alt = 3 then aastocks.stkcost3 * aastocks.cartonratio3
when v_est_joblist.alt = 4 then aastocks.stkcost4 * aastocks.cartonratio4
when v_est_joblist.alt = 5 then aastocks.stkcost5 * aastocks.cartonratio5
END
ELSE 0
END AS ecostmats,
0 as ecostlab,
0 as eprochours,
0 AS bcostmats,
0 AS bcostlab,
0 AS bprochours,
0.0 AS acostmats,
0.0 AS acostlab,
0 AS aprochours,
0.0 AS chgcost,
0 AS dirlab,
0.0 AS aacost,
0.0 AS pecost,
0.0 AS otcost,
0 AS sell
FROM dbo.aapthead
INNER JOIN dbo.aastocks on aapthead.estno = aastocks.estno and aapthead.partno = aastocks.partno
INNER JOIN dbo.v_est_joblist on v_est_joblist.estno = aapthead.estno
LEFT OUTER JOIN dbo.ssinvent on aastocks.matno = ssinvent.matno
AND ssinvent.invtype IN ('R', 'S')
LEFT OUTER JOIN dbo.ssdept ON ssdept.deptno = (CASE WHEN aastocks.houvend = 'H' THEN ssinvent.deptno
ELSE CASE WHEN aapthead.parttype = 'S' THEN #opt_stockccs
ELSE #opt_stockccr
END
END)
WHERE v_est_joblist.ljob = #tcLjob
AND 1 = [dbo].[DivisionCheck] (ssdept.divno,#tcDivNo)
In the three code samples shown, I see nothing that would generate that subquery error message.
exists() subqueries are designed to handle multilple returned rows
The select max() subquery is properly formed
There is nothing that would cause problems if the only subquery in the third block returned multiple rows
Ergo, it must be something else. My guess—and this is a guess—is that, in the third bock, tables v_est_joblist and/or v_depuse_qty are in fact views (I use something like the v_Blah convention myself), and something’s going wrong in one of them. If so, you’d need to test these views, see what they’re returning when joined into the main query as they are.
It appears that your "in" expression is being supplied with more than one row of values. So, although "exists" can accept multiple rows as input, "0 in (aastk.cartonratio1, ...) cannot.

SQL Server 2012 and NULL comparison

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

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