AND/OR based on variable value in stored procedures - sql-server

I would like to use AND/OR between the conditions in a stored procedure, and the decision is dependent on the parameter value whether it was 0 (AND) or 1 (OR)
Can anyone help me with this please, i guess this is an easy thing to do but i can't seem to figure it out. Thanks

The easiest way (on first glance) would be to concatenate the query string using dynamic SQL, but dynamic SQL has its issues.
See The Curse and Blessings of Dynamic SQL for an in-depth explanation.
So I would try to avoid dynamic SQL, which is no big deal if your queries are not too complex.
The easiest way is just to fire two different queries depending on the parameter value:
CREATE PROCEDURE spTest
#AndOr bit
AS
BEGIN
if #AndOr = 0 begin
select * from YourTable where foo = 1 and bar = 2
end
else begin
select * from YourTable where foo = 1 or bar = 2
end
END
This is of course an example with a very simple query.
If you have lots of queries, or if your queries are very complex, this might not be the best solution because it forces you to duplicate all queries...but as always, it depends :-)

You can implement your logic on a CASE statement. Something like this:
CREATE PROCEDURE dbo.MySP #OrAnd BIT
AS
BEGIN
SELECT *
FROM MyTable
WHERE CASE WHEN Condition1 AND Condition2 AND #OrAnd = 0 THEN 1
WHEN (Condition1 OR Condition2) AND #OrAnd = 1 THEN 1 ELSE 0 END = 1
END

If you convert the simple conditions' boolean results into numeric ones (0 or 1), you will be able to use your parameter in the following way:
(
(CASE WHEN condition1 THEN 1 ELSE 0 END ^ #AndOr)
&
(CASE WHEN condition2 THEN 1 ELSE 0 END ^ #AndOr)
) ^ #AndOr = 1
Here #AndOr is your parameter, ^ is the Transact-SQL bitwise exclusive OR operator, & stands for the bitwise AND in Transact-SQL, and the CASE expressions are used to convert the boolean results into 0 or 1.
If #AndOr = 0 (which means we want AND between the conditions), the above expression effectively boils down to this:
case1 & case2 = 1
because X XOR 0 yields X and so neither individual values of case1 and case2 nor the entire result of the & operator are not affected by the ^ operators. So, when #AndOr is 0, the result of the original expression would be equivalent to the result of condition1 AND condition2.
Now, if #AndOr = 1 (i.e. OR), then every ^ operator in the expression returns the inverted value of its left operand, in other words, negates the left operand, since 1 XOR 1 = 0 and 0 XOR 1 = 1. Therefore, the original expression would essentially be equivalent to the following:
¬ (¬ case1 & ¬ case2) = 1
where ¬ means negation. Or, converting it back to the booleans, it would be this:
NOT (NOT condition1 AND NOT condition2)
According to one of De Morgan's laws,
(NOT A) AND (NOT B) = NOT (A OR B)
Applying it to the above condition, we get:
NOT (NOT condition1 AND NOT condition2) = NOT (NOT (condition1 OR condition2)) =
= condition1 OR condition2
So, when #AndOr is 1, the expression given in the beginning of my answer is equivalent to condition1 OR condition2. Thus, it works like expected based on the value of #AndOr.

Having the input parameter you can use a IF clause to make different selects.
If input parameter = 0 make the AND conditions, otherwise make the OR conditions.

I can't see any particular elegant way to do it. So here's the straightforward approach
create function myfun (#parm1 int, #parm2 int, #andor int) returns int
begin
if (#andor = 0 AND #parm1 = 99 AND #parm2 = 99) return 1
else if (#andor = 1 AND (#parm1 = 99 OR #parm2 = 99)) return 1
return 0
end
go
select dbo.myfun(99,98,0) -- AND condition should return 0
select dbo.myfun(99,98,1) -- OR condition should return 1
select dbo.myfun(98,98,0) -- AND condition should return 0
select dbo.myfun(98,98,1) -- OR condition shoujld return 0

Related

Store Operators like >= OR <= in Microsoft Access

I have certain values that are needed for validation in Forms, either they look like Value X >= 0 but it could also be X <= 0, it depends on what operator should be used. How can I store such a value?
(I use MS SQL Server + Access as Frontend)
I basicly wanna store the Value and if it needs to be bigger than or smaller than.
Store the value, as usual, in a field, and the operator in another field as Short Text.
The you can use Eval:
Result = Eval("" & [ValueField] & [OperatorField] & "0")
You can store your value as it is, and to check if this value is positive or not you have two ways
First one
Create a computed column to check the Value column as
CREATE TABLE YourTable(
YourValue INT,
IsPositive AS CASE WHEN YourValue < 0 THEN 0 ELSE 1 END
);
INSERT INTO YourTable (YourValue) VALUES
(1), (-1);
SELECT *
FROM YourTable;
Second one
Use a CASE expression (or even you can create a view) as
SELECT CASE WHEN YourValue < 0 THEN 0 ELSE 1 END IsPositive,
--...
FROM YourTable;

Why is '-' equal to 0 (zero) in SQL?

When you run the following query in SQL Management studio the result will be 1.
SELECT
CASE WHEN '-' = 0 THEN
1
ELSE
0
END
That scares me a bit, because I have to check for 0 value a numerous number of times and it seems it is vulnerable for being equal to value '-'.
You're looking at it the wrong way around.
'-' is a string, so it will get implicitly casted to an integer value when comparing it with an integer:
select cast('-' as int) -- outputs 0
To make sure that you are actually comparing a value to the string '0', make your comparison like this instead:
select case when '-' = '0' then 1 else 0 end
In general, you're asking for trouble when you're comparing values of different data types, since implicit conversions happen behind the scene - so avoid it at all costs.

CASE Statement SQL: Priority in cases?

I have a general question for when you are using a CASE statement in SQL (Server 2008), and more than one of your WHEN conditions are true but the resulting flag is to be different.
This is hypothetical example but may be transferable when applying checks across multiple columns to classify data in rows. The output of the code below is dependant on how the cases are ordered, as both are true.
DECLARE #TESTSTRING varchar(5)
SET #TESTSTRING = 'hello'
SELECT CASE
WHEN #TESTSTRING = 'hello' THEN '0'
WHEN #TESTSTRING <> 'hi' THEN '1'
ELSE 'N/A'
END AS [Output]
In general, would it be considered bad practice to create flags in this way? Would a WHERE, OR statement be better?
Case statements are guaranteed to be evaluated in the order they are written. The first matching value is used. So, for your example, the value 0 would be returned.
This is clearly described in the documentation:
Searched CASE expression:
Evaluates, in the order specified, Boolean_expression for each WHEN clause.
Returns result_expression of the first Boolean_expression that evaluates to TRUE.
If no Boolean_expression evaluates to TRUE, the Database Engine returns the else_result_expression if an ELSE clause is specified, or
a NULL value if no ELSE clause is specified.
As for whether this is good or bad practice, I would lean on the side of neutrality. This is ANSI behavior so you can depend on it, and in some cases it is quite useful:
select (case when val < 10 then 'Less than 10'
when val < 100 then 'Between 10 and 100'
when val < 1000 then 'Between 100 and 1000'
else 'More than 1000' -- or NULL
end) as MyGroup
To conclude further - SQL will stop reading the rest of the of the case/when statement when one of the WHEN clauses is TRUE. Example:
SELECT
CASE
WHEN 3 = 3 THEN 3
WHEN 4 = 4 THEN 4
ELSE NULL
END AS test
This statement returns 3 since this is the first WHEN clause to return a TRUE, even though the following statement is also a TRUE.

Is there an Alternate for Where is Null using Where = Null?

If not, is there an alternate way to switch through SELECT statements using a CASE or IF/THEN identifier WITHOUT putting the statement in a scalar variable first?
Is there a way to format this without using IS and using an = sign for it to work?
SELECT ID FROM TABLE WHERE ID = Null
No. NULL isn't a value. Think of NULL as a condition, with IS NULL or IS NOT NULL is testing for this condition.
In this example you can test for the actual value, or lack of value represented by a conditon
WHERE
(X IS NULL OR X = #X)
OR
WHERE
(#X IS NULL OR X = #X)
Or test for your definite conditions first:
WHERE
CASE X
WHEN 1 THEN
WHEN 2 THEN
ELSE -- includes NULL
END = ...
Your question is abstract so hard to give a more precise answer.
For example, are you having problems with NOT IN and NULL? If so, use NOT EXISTS.

Check Constraints and Case Statement

I need help with this check constraint, I get the following error message: "Msg 102, Level 15, State 1, Line 14
Incorrect syntax near '='."
Or maybe the question I should ask is if this is possible using a check constraint
What I am trying to achieve is: If InformationRestricted is True, InformationNotRestricted cannot be true and InformationRestrictedFromLevel1, InformationRestrictedFromLevel2, InformationRestrictedFromLevel3, InformationRestrictedFromLevel4, InformationRestrictedFromLevel5 cannot be true
I am not trying to assign values to the columns, just trying to ensure the values of the columns = 0 (i.e. false) if InformationRestricted is True
Here is the script:
CREATE TABLE EmployeeData
(FirstName varchar(50),
Last Name varchar(50),
Age int,
Address varchar(100),
InformationRestricted bit,
InformationNotRestricted bit,
InformationRestrictedFromLevel1 bit,
InformationRestrictedFromLevel2 bit
InformationRestrictedFromLevel3 bit
InformationRestrictedFromLevel4 bit
InformationRestrictedFromLevel5 bit);
ALTER TABLE EmployeeData ADD CONSTRAINT ck_EmployeeData
CHECK (CASE WHEN InformationRestricted = 1 THEN InformationNotRestricted = 0 --InformationRestricted is true, InformationNotRestricted is false
AND( InformationRestrictedFromLevel1 = 0 --is false
OR InformationRestrictedFromLevel2 = 0 --is false
OR InformationRestrictedFromLevel3 = 0 --is false
OR InformationRestrictedFromLevel4 = 0 --is false
OR InformationRestrictedFromLevel5 = 0)); --is false
A CASE expression is something that returns a value of a particular data type (the type to be determined by the various datatypes of each THEN clause).
SQL Server doesn't have a boolean data type, so you can't return the result of a comparison operation.
Try adding additional comparisons into WHEN clauses, and having the THENs return either 1 or 0, if you want to allow or disallow the outcome (respectively). Then compare the overall result to 1.
I can't parse out the sense of your condition entirely, but something like:
CHECK(CASE WHEN InformationRestricted = 1 THEN
CASE WHEN InformationNotRestricted = 0 AND
(InformationRestrictedFromLevel1 = 0 --is false
OR InformationRestrictedFromLevel2 = 0 --is false
OR InformationRestrictedFromLevel3 = 0 --is false
OR InformationRestrictedFromLevel4 = 0 --is false
OR InformationRestrictedFromLevel5 = 0)
THEN 1
ELSE 0
END
--Other conditions?
END = 1)
My confusion is I'd have though you'd want to check that one and only one of the InformationRestrictedFromXXX columns would be one. In fact, from the general description, (without knowing more about your problem domain), I'd have probably just created a column InformationRestrictionLevel, of type int, with 0 meaning unrestricted, and higher values indicating the level it's restricted from.
Looks like you're not closing the case with end. The basic format of a check constraint using case is:
check(case when <condition> then 1 else 0 end = 1)
If you nest multiple cases, be sure to match the number of cases with the number of ends:
check
(
1 =
case
when <condition> then
case
when <condition> then 1
else 0
end
else 0
end
)
Formatting all elements of the same case with the same indentation can be a big help.

Resources