Using IF/THEN or CASE within a WHERE clause in SQL - sql

I have been assigned the task of updating the EEO survey and reporting for a mid sized company. I am working on a stored procedure to populate a report from. All is good but for a syntax problem. One of the requirements is to dynamically allow the user to filter the results by the EEO Job Group Number. When the report page loads, it populates the table with all Job Groups Combined. I have placed a DropDownList on the page that allows the user to choose one of the 10 EEO Job Groups or by default, All Job Groups Combined (no filtering). The DDL executes postback and populates a parameter; #intEeoJobGroupID. There is not actually a 0 ID value in the table, just in the DDL. I want the (usp) query to use one set of WHERE statements if the passed parameter #intEeoJobGroupID = 0, and another if #intEeoJobGroupID <> 0. (Effectively adding another AND statement if the parameter <> 0)
I want to return the count of how many EEO records meet the requirements of the query. I have tried IF/THEN, and CASE, in many different formats, and can not seem get the syntax right. In the example below I get the message "Incorrect Syntax near the first = in the THEN statement, as well as the keyword ELSE.
Any hints?
DECLARE #intEeoJobGroupID INT
SELECT
COUNT (E.intEeoID)
FROM
dbo.NewEEO AS E
WHERE
CASE WHEN #intEeoJobGroupID = 0
THEN
E.intGenderID = 1
AND E.intRaceID = 2
ELSE
E.intGenderID = 1
AND E.intRaceID = 2
AND E.intEeoJobGroupID = #intEeoJobGroupID

You're making it way too complicated:
WHERE E.intGenderID = 1
AND E.intRaceID = 2
AND (E.intEeoJobGroupID = #intEeoJobGroupID OR #intEeoJobGroupID = 0)
As someone else already mentioned, your existing syntax was missing an "END", but it still won't work with that added. To get this right in the future, one thing you can try to do is remember that CASE expressions in SQL are just that: expressions. They are not statements, as you might be used to with if statements in c# code. You don't use CASE for flow control, to define blocks as you were trying to do.

Don't try to return a boolean from a CASE statement. Instead return some value that is then checked outside the CASE statement (and so then resulting in a boolean).
CASE WHEN #mode = 1 THEN CASE WHEN <Condition1> THEN 1 ELSE 0 END
WHEN #mode = 2 THEN CASE WHEN <Condition2> THEN 1 ELSE 0 END
END
=
1
Note: This will create Awful execution/explain plans and totally nerf performance. You are better using real IF blocks and real queries, or possibly unions...
IF #mode = 1
SELECT foo FROM bar WHERE <Condition1>
ELSE IF #mode = 2
SELECT foo FROM bar WHERE <Condition2>
Or...
SELECT foo FROM bar WHERE <condition1> AND #mode = 1
UNION ALL
SELECT foo FROM bar WHERE <condition2> AND #mode = 2
In order to prevent massive duplication of code, you may find that encapsulating the bulk of the query in a VIEW is helpful.

You can't make a comparison the result of a case condition. If you're using case in a where clause, it needs to be on one side of the operator:
CASE #case_value
WHEN 0 THEN
some_column
ELSE
some_other_column
END = #some_value
However, if you try to make your actual condition fit this rule, you'll end up not using the case statement at all, as #Joel point out.

You have to add
end
in the end of case.

Related

How to implement nested CASE statements in a SQL Select of a stored procedure?

I have a SELECT statement from a temp table in a stored procedure that selects these two columns:
DECLARE #Mode INT
CASE
WHEN t.Descr = '-- Prior Balance --'
THEN ''
ELSE t.ChgAmount
END AS ChgAmount,
CASE
WHEN t.Descr = '-- Prior Balance --'
THEN ''
ELSE t.PayAmount
END AS PayAmount,
I want to conditionally return those two columns differently depending on the value of #Mode, specifically if it is equal to 7.
I'm getting confused about the levels of nesting that I need and the formatting of doing this.
So far I have tried something like this:
CASE
WHEN #Mode = 7
THEN
CASE
WHEN t.TranDate > #Due
THEN t.ChgAmount
END
END AS CurrentCharges,
CASE
WHEN t.Descr = '-- Prior Balance --'
THEN ''
ELSE t.ChgAmount
END AS ChgAmount,
CASE
WHEN t.Descr = '-- Prior Balance --'
THEN ''
ELSE t.PayAmount
END AS PayAmount,
The above SELECT might work, but it would stil return the extra column. How should I nest the other, original, CASE statement for the ChgAmount?
First, let's clarify in abstract terms that in the following pseudo-code
If X then
Case 1
If Y then
Case 2
Else
Case 3
End If
Else
Case 4
End If
Case 1 is equivalent to "X is true"
Case 2 is equivalent to "X is true and Y is true"
Case 3 is equivalent to "X is true and Y is false"
Case 4 is equivalent to "X is false"
Furthermore, let's clarify that case-when criterias are logically very similar to our pseudo-code presented above of if-then, hence, you can apply composite criteria instead of nesting case-when if you prefer that, but also, you can implement nested case-when criterias, it's a matter of style.
As a result, you will need to formulate the logic you want to apply, by asking yourself the following questions:
do I need the same number of fields in my different cases? (if not, then you will probably need to write different queries in different cases)
what cases do I have for my fields if #Mode is 7?
what cases do I have for my fields if #Mode is not 7?
how can I merge my criterias in the points above into coherent (composite) criterias that would not require nesting?
If you answer these questions as an edit to this question, then we will be able to more properly answer your questions than the general terms I'm using in this answer and we may provide code for you as well. However, if you think this through, then you might also be able to implement this in a not nested way. And, if you are able to understand this as far as to implement it into a not nested way, then you could transform that implementation into a nested implementation as well.

Oracle CASE missing right parenthesis for a "in" limit

I have a QRY im developing in Oracle for spotfire. In the where statement, I have a decision case statement and if its True, im trying to pass a list of items to match a column, below is what I have, but its throwing a missing right parenthesis error and I cannot determine why. In short, when a variable is determined True (in this case 9>8 for the example, I need it to result those items, else, result the entire column with no limits.
Note: This works fine when its only 1 item being passed, i.e. 'BOB' but as soon as its multiple, this error occurs.
and Column1 = (CASE When 9>8 Then ('BOB','TOM') Else Column1 END)
Case expressions are best avoided in the where clause. Instead, write the logic with AND and OR:
And (
(9>8 AND Column1 IN ('BOB','TOM'))
OR 9<=8 -- You say you check a variable here, don't forget to check for NULL
)
Oracle does not have a boolean type for use in SQL queries.
Instead, just use basic logic:
and ( (9 > 8 and Column1 in ('BOB','TOM')) or
9 <= 8
)

Is there a more concise version of the CASE WHEN foo.bar IS NULL THEN 0 ELSE 1 END paradigm?

The goal is simply to check whether a field is NULL or not, yielding a value of 1 when the field is not NULL, and 0 otherwise. I can't use COALESCE here, because if the field isn't NULL, I just want to return 1, not the value of the field.
I have a gut feeling that there's already a function that does this - something like NULL_STATUS_INTEGER(foo.bar), for example - instead of needing to write out the full CASE statement of CASE WHEN foo.bar IS NULL THEN 0 ELSE 1 END. I'm not saying that the CASE version is absurdly long, but I just find it strange that there's not a shorter way to do this.
I'm primarily interested in solutions that aren't confined to one SQL vendor, but I happen to be using SAS, if there happens to be a SAS-specific way to do this in PROC SQL.
Comparisons are also boolean, return a true/false
proc sql;
create table want as
select *, name = 'Alfred' as flag
from sashelp.class;
quit;
data want;
set sashelp.class;
flag = (name = 'Alfred');
run;
EDIT:
Another option is to use IFN()/IFC(), which factor in three conditions, FALSE, TRUE, MISSING. IFN()/IFC() can be used in SQL or a data step in SAS.
x = ifc(name="Alfred", 1, 0, .);

Defining the (alias) name of a field

I've been looking over some code in an old Classic ASP system of ours that builds its own SQL within the stored procedure and then executes it {shudders}.
Several of the SELECTion lines contain an assignment, similar to:
SELECT
my_field = CASE WHEN value = whatever THEN 1 ELSE 0 END
...
Is there any difference (or anything I need to be aware of) between this and using a standard AS alias?...
SELECT
CASE WHEN value = whatever THEN 1 ELSE 0 END AS my_field
...
No, the following code is all synonymous:
SELECT one = 1;
SELECT 1 one;
SELECT 1 AS one;
SELECT 'one' = 1; --this is deprecated, don't use it.
Which you use (apart from the last), is normally down the preference. Personally, I use AS. One reason is I can then easily tell queries that return datasets, and those that assign values to variables a part.
The 2 examples that you have given are identical. However, when you go through the old code you might also find a variant with an # sign before my_field, like this:
SELECT
#my_field = CASE WHEN value = whatever THEN 1 ELSE 0 END
In this case a varable called #my_field is assigned a value, but nothing is SELECTed. This you can not rewrite to the other syntax using AS #myfield.

Condition that decides whether to execute WHERE or not

I have a really big stored procedure and I'd like to include a WHERE clause at the end to be executed just in case that #myparameter=1. I don't want the stored procedure to pay any attention to the WHERE clause when #myparameter=0. Is there any way to do this with CASE or something like that?
WHERE #myparameter=0 OR (insert the current conditions here)
add a modified version of where instead of yours
where (#myparemeter = 0 or (#myparameter = 1 and (your where conditions here)))
If you really want to use CASE, something like this ...
WHERE
CASE
WHEN (#myparameter=0) THEN 1
WHEN (#myparameter=1) AND (rest of where clause) THEN 1
ELSE 0
END = 1