SQL Server WHERE clause : column IS NULL or column = parameter value - sql

The code snippet below is what I'm trying to achieve, but I'm having trouble making it work. If the parameter that gets passed into the procedure is null, I want to only return the rows with a WHERE clause IS NULL, but if there is a value, I want to return the rows that are equal to the value passed in. Dynamic SQL seems like it would work, but I'm curious if there's an easier way I'm missing. Thanks in advance.
PARAM:
#id varchar(10) = '123456789'
SELECT *
FROM TABLE T
WHERE
CASE
WHEN #id IS NULL THEN (id IS NULL)
ELSE id = #id
END

The logic you want is:
WHERE (#id IS NULL AND id IS NULL) OR
id = #id

You're trying to use a CASE expression like a Case (Switch) statement. Switches don't exist in T-SQL, and a CASE expression returns a scalar value not a boolean result.
Don't, however, use CASE expressions in the WHERE, use proper Boolean logic:
SELECT *
FROM YourTable YT
WHERE (ID = #ID
OR (ID IS NULL AND #ID IS NULL))

Related

Is there a way to use in or = with a case statement in the where clause in sql?

I have a stored procedure that may or may not get a string list of int ids. When it doesn't get it the value is: ' '. Other wise its something like this: '500,507,908'
I'm trying to use it like this:
select ID as projectTeamId, Employee_ID, Supervisor_ID
from ProjectTeam
where Project_ID = #projectId and IsDeleted = 0 and
ID in (CASE #stringList WHEN '' THEN ID ELSE (SELECT * from TurnListStringIntoTable(#stringList)) END)
to get a result set but it errors out with this code when the string list comes in blank:
An error has occurred while processing Report 'MassReleaseHoursReport':
Subquery returned more than 1 value. This is not permitted when the subquery follows =, !=, <, <= , >, >= or when the subquery is used as an expression.
I know its an issue where id needs to = id instead of being in id. Any ideas on how I can have the case statement work with #stringList = '' then id = id else id in (SELECT * from TurnListStringIntoTable(#stringList))?
TurnListStringIntoTable returns a table from a string list which in my case is just the project Team ID
I would recommend boolean logic rather than a case expression:
where
Project_ID = #projectId
and IsDeleted = 0
and (
#stringList = ''
or id in (select * from TurnListStringIntoTable(#stringList))
)
Unrelated side note: if you are running SQL Server, as I suspect, and your version is 2016 or higher, you can use built-in function string_split() instead of your customer splitter.
Sure!
All you have to do is use the parameterless flavor of case:
select *
from my_table t
where t.col_1 = case
when #var in (1,2,3) then "foo"
when #var = 4 then "bar"
when #var in (5,6,7) then "baz"
else "bat"
end
One might note that the when expressions are not limited to looking at the same variable in any way. The only requirement is that they have to be a boolean expression. They're evaluated in order top to bottom. The first when clause that evaluates to true wins and it's then value is returned.
If there's no else and evaluation fails to find a match, the result value is null.
Your problem though, is that case expressions
return a single value, and
that value must be of the same type. Can have it returning a string in some cases and a table variable in another.
So... your where clause should look something like this:
where ...
and 'true' = case
when #stringList = '' then 'true'
when ID in ( select *
from TurnListStringIntoTable(#stringList)
) then 'true'
else 'false'
end
You'll probably find, too, that invoking a user-defined function to convert a comma-delimited string into a table variable within the where clause is probably a Bad Idea™ due to the performance impact that that will have.
You'd be better off to move your TurnListStringIntoTable call outside of the select statement, thus:
declare #list = TurnListStringIntoTable(#stringlist)
select ...
from ProjectTeam pt
where . . .
and #stringlist = ''
OR exists ( select * from #list x where x.ID = pt.ID )

Select all records where function is false

I have a validation function that I'd like to run on each id in a table, and return only those ids which are invalid. Code that I've tried:
select myID
from myDB.dbo.myTable
where (myDB.dbo.validateID(myID) = 0)
Where myDB.dbo.validateID is a scalar-valued function.
This works but returns null for all the valid IDs - I want to only return the invalid ones. What is the most efficient way to return all the invalid rows using this function?
Update:
The validateID function returns 1 if the ID is valid, 0 if it isn't.
My code above returns null if the ID is valid, and the ID if it's not. I want it to instead only return the invalid IDs, without all the null results.
select myID
from myDB.dbo.myTable
where (myDB.dbo.validateID(myID) = 0)
This works but returns null for all the valid IDs
That is simply not possible. If you select myID, you get myID, so if you get null, then myID must be null, and your validateID function is detecting that as invalid.
If your function should treat null as a valid ID, you need to fix your function so that myDB.dbo.validateID(null) returns 1.
If your function should treat null as neither a valid nor an invalid ID, you need to fix your function so that myDB.dbo.validateID(null) returns null.
If your function should treat null as an invalid ID, but you still want to exclude null results, when just add a condition myID is not null to your selection.
I tried the same logic but am getting proper output as expected. Am getting rows returned only when function outputs 0 (ie) Invalid records. I don't see NULL values for valid records.
create table dbo.Contracts1(id int,name varchar(50),pric int)
INSERT INTO dbo.Contracts1
VALUES ( 1,'invalid',40000),
(2,'valid',50000),
(3,'valid',35000),
(4,'invalid',40000)
CREATE FUNCTION Testt(#id INT)
returns INT
AS
BEGIN
DECLARE #ret INT
IF EXISTS (SELECT 1
FROM dbo.Contracts1
WHERE name = 'Valid'
AND id = #id)
SELECT #ret = 1
ELSE
SELECT #ret = 0
RETURN #ret
END
SELECT *
FROM dbo.Contracts1
WHERE dbo.Testt(id) = 0
OUTPUT
id name pric
-- ------- -----
1 invalid 40000
4 invalid 40000

Bit parameter with Null value in Stored Procedure

I'm having a bit value in my table, which contains bit (0 or 1) and NULL (as default).
Here is my SProc:
CREATE PROCEDURE msp_CustomerStatistics
#Postal_MinValue int,
#Postal_MaxValue int,
#SubscriberState bit,
#CustomerType varchar(50)
BEGIN
[...]
WHERE Sub = #SubscriberState
AND Postal BETWEEN #Postal_MinValue AND #Postal_MaxValue
AND CustType = #CustomerType
END
When I pass the #SubscriberState parameter with 1 or 0, the result is correct.
But when I pass null, the result is 0, which ain't correct.
If I create a SQL select with following where clause:
WHERE Sub IS NULL
Then the result shows the correct count.
Any idea how I make my Stored Procedure working with NULL parameter in my WHERE clause too??
You can not use the = operator with null values. Comparisons with NULL always return false. Try to modify your WHERE statement to the following:
WHERE (Sub = #SubscriberState OR (#SubscriberState IS NULL AND Sub IS NULL))
You could either set null values to 0 and check it like this:
WHERE Isnull(Sub,0) = #SubscriberState
or have a tri-state sort of bodge like:
WHERE Isnull(Sub,3) = isnull(#SubscriberState,3)

How to write filtered queries using SQL stored procedures?

How can I write a SQL stored procedure where I want the parameters to be optional in the select statement?
try this.. Make the SPs input parameters that control the filtering optional, witrh default values of null. In each select statement's Where clause, write the predicate like this:
Create procedure MyProcedure
#columnNameValue [datatype] = null
As
Select [stuff....]
From table
Where ColumnName = Coalesce(#columnNameValue , ColumnName)
this way if you do not include the parameter, or if you pass a null value for the parameter, the select statement will filter on where the column value is equal to itself, (effectively doing no filtering at all on that column.)
The only negative to this is that it prevents you from being able to pass a null as a meaningfull value to explicitly filter on only the nulls.... (i.e., Select only the rows where the value is null) Once the above technique has been adopted, you would need to add another parameter to implement that type of requirement. ( say, #GetOnlyNulls TinyInt = 0, or something similar)
Create procedure MyProcedure
#columnNameValue [datatype] = null,
#GetOnlyNulls Tinyint = 0
As
Select [stuff....]
From table
Where (ColumnName Is Null And #GetOnlyNulls = 1)
Or ColumnName = Coalesce(#columnNameValue , ColumnName)

SQL Statement Match Anything

I use a regex in my SQL statements for an app that look like this
SELECT * FROM table WHERE id = {{REPLACEME}}
However, sometimes I'm not giving a parameter to replace that string with. Is there a way to replace it with something that matches anything. I tried *, but that does not work.
SELECT * FROM table WHERE id = id will match all rows that have non-null id
SELECT * FROM table WHERE id = id OR id IS NULL will match all rows.
id is probably a primary key, so you can probably use the former.
Replace {{REPLACEME}} with
[someValidValueForYouIdType] OR 1=1
I can only describe my solution with an example. The #AllRec is a parameter:
Declare #AllRec bit
set #AllRec = {0|1} --as appropriate
SELECT *
FROM table
WHERE
(
id = {{REPLACEME}}
and #AllRec = 0
) OR (
#AllRec = 1
)
In this solution, if #AllRec is 1 then everything is returned, ignoring the id filter. If #AllRec is zero, then the id filter is applied and you get one row. You should be able to quickly adapt this to your current regex solution.
Using the Regex-Replace option opens you up to SQL Injection attacks.
Assuming your language has support for parameterized queries, try this modified version of Jacob's answer:
SELECT * FROM table WHERE (id = #id OR #id IS NULL)
The catch is that you'll always have to provide the #id value.
SELECT field1, field2
FROM dbo.yourTable
WHERE id = isnull(#var, id)
Not sure what language your using, and this code kind of scares me but...
var statement = "SELECT * FROM table";
If REPLACEME is not empty Then
statement += " WHERE id = {{REPLACEME}}"
End If