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)
Related
We have a scalar function in our application as below
CREATE function dbo.SCMGetEnvProfileValueFn
(#HierarchyCode varchar(255), -- Usually the subsystem code
#Code varchar(50), -- The Code to find
#Default varchar(255) -- If not found, return this default
)
RETURNS varchar(255)
AS
BEGIN
DECLARE #Value as varchar(255)
SELECT #Value = (SELECT TOP(1) Value FROM HVCEnvProfile
WHERE HierarchyCode = #HierarchyCode
AND Code = #Code)
RETURN ISNULL (#Value, #Default)
END
We converted this function to Table function
CREATE FUNCTION SCMGetEnvProfileValueTblFn
(#HierarchyCode varchar(255), -- Usually the subsystem code
#Code varchar(50), -- The Code to find
#Default varchar(255) -- If not found, return this default
)
RETURNS TABLE
AS
RETURN
(SELECT TOP(1) ISNULL (Value, #Default) AS value
FROM HVCEnvProfile
WHERE HierarchyCode = #HierarchyCode AND Code = #Code)
Below 2 statements shows different output. We do not have a column in the table HVCEnvProfile for this entry. Why the variable #Value is showing NULL when there is no row in the table.
SELECT value
FROM dbo.SCMGetEnvProfileValueTblFn('Registration', 'AdmitDtmEffectsLocationHistory', 'TRUE')
SELECT dbo.SCMGetEnvProfileValueFn('Registration', 'AdmitDtmEffectsLocationHistory', 'TRUE')
If a query returns no rows, then no rows will be returned, wrapping an ISNULL won't change that. Example:
SELECT ISNULL(V.C,0)
FROM (VALUES(1),(2),(3),(4))V(C)
WHERE V.C = 5;
Notice this does not return 0, but nothing.
You need to wrap the entire query in an ISNULL.
CREATE FUNCTION SCMGetEnvProfileValueTblFn (#HierarchyCode varchar(255), -- Usually the subsystem code
#Code varchar(50), -- The Code to find
#Default varchar(255) -- If not found, return this default
)
RETURNS table
AS
RETURN
SELECT ISNULL((SELECT TOP (1) [Value]
FROM HVCEnvProfile
WHERE HierarchyCode = #HierarchyCode
AND Code = #Code
ORDER BY SomeColumn), #Default) AS [Value]; --Don' forgot to change the value of SomeColumn
Don't forget, as well, you need an ORDER BY when using a TOP unless you're "happy" with inconsistent results (which I doubt), so i have added one that you will need to amend.
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))
Quick question on Coalesce:
clw.ClawbackPercent = Coalesce(#ClawbackPercent, clw.ClawbackPercent)
Lets say for column 'ClawbackPercent' I have a value of 100.
If I execute a proc and set parameter #ClawbackPercent to have the value NULL, it keeps the value 100 in the row for that column which is great.
However, if I want to set 100 to actually be NULL, what do I need to write in the exec proc statement or what do I need to add in the Coalesce statement?
Thank you
It sounds like you want 100 to be the Default value of a stored proc parameter, not necessarily to replace all NULLs with this value. If this is the case, you don't want a COALESCE but you do need to provide a default value for the parameter on the proc definition.
e.g.
CREATE PROC dbo.MyProc (
#MyParam INT = 100
)
AS
-- My code here
If somebody executes this proc without specifying a value for #MyParam, the default of 100 will be assigned. If they explicitly specify #MyParam = NULL then NULL will be assigned..
Then probably you should not use coalesce, instead you can use case statement as below:
clw.ClawbackPercent = CASE WHEN #ClawbackPercent = 100
THEN NULL
ELSE
#ClawbackPercent END
in the select statement
You have to write in the following way:
CREATE PROCEDURE sp_test(
#ClawbackPercent VARCHAR(30)
) AS
BEGIN
SELECT COALESCE(#ClawbackPercent, '100')
END
--call as below and if you want to return second value then, EXEC sp_test NULL
--if your second, thirds... parameters are in INTEGER then simply CAST to VARCHAR
EXEC sp_test 'NULL'
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)
I am passing parameters into a stored procedure. The one parameter is a varchar(50) that can be a string like " > 5000" and " <= 10000".
Here is some of the code:
....
....
#colourid int = 0,
#regionid int = 0,
#sellingPrice varchar(50) = '-1'
AS
SELECT
....
....
WHERE
(dbo.tbl_Listings.fld_ColourID = CASE WHEN #colourid = 0 THEN dbo.tbl_Listings.fld_ColourID ELSE #colourid END)
AND (dbo.tbl_Listings.fld_RegionID = CASE WHEN #regionid = 0 THEN dbo.tbl_Listings.fld_RegionID ELSE #regionid END)
AND
How do I add #sellingPrice to the WHERE? I can't mimic how it was done for the int parameters because it's not always going to use =. I need to say "if selling price is not -1 then fld_SellingPrice #sellingPrice".
The only way you can achieve that is by using dynamic SQL, building up your query in a local variable and then executing it via (preferably) sp_executesql.
So something like
DECLARE #sql nvarchar(MAX)
SET #sql = 'SELECT .... WHERE ' + #sellingPrice
sp_executesql #sql
However, this really does open you up to the possibility of SQL injection, and therefore you have to either
a. Be very sure that the procedure will only be called by callers you trust fully
b. Add protection for badly formed parameters within your procedure, which is much harder than it sounds
c. Find a different way to approach the problem entirely.
If you know you are going to have a general set of comparisons to use, I would create a parameter per comparison in your SP and use them as needed. So your SP might have
#greaterThan int,
#lessThan int,
#equalTo int
Then in the SP you could do
if #greaterThan IS NULL
SELECT #greaterThan = MAX(field) FROM table -- or some arbitrary value that will always evaluate to true
if #lessThan IS NULL
SELECT #lessThan = MIN(field) FROM table
Then just use those in your WHERE clause. Otherwise, as posted, you're going to have to do dynamic SQL by building an SQL string with the pieces of the WHERE clause.
I would use a from and a to variable.
So when you want less than 5000, you set to variable = 5000 and leave from blank
....
....
#colourid int = 0,
#regionid int = 0,
#fromsellingprice int = 5000
#tosellingprice int = null
AS
SELECT
....
....
WHERE
(dbo.tbl_Listings.fld_ColourID = CASE WHEN #colourid = 0 THEN dbo.tbl_Listings.fld_ColourID ELSE #colourid END)
AND (dbo.tbl_Listings.fld_RegionID = CASE WHEN #regionid = 0 THEN dbo.tbl_Listings.fld_RegionID ELSE #regionid END)
AND
sellingPrice >= coalesce(#fromsellingprice, sellingprice)
and sellingPrice <= coalesce(#tosellingprice, sellingprice)
You can't do this directly in SQL - the parameter will not be parsed and interpreted as part of the query with predicates.
The only way to do this (passing in the operator) is to use dynamic SQL, which comes with its own pitfalls.
You may consider passing in a parameter for what operator to use and have a bunch of if sections for each supported parameter - this may be worse than dynamic SQL.