conditional stored procedure with/without passing parameter - sql

I created a stored procedure which when passed nothing as parameter should return the entire table. But if the studentId is passed, then return her details.
Something like this
create procedure usp_GetStudents #studentId int = null
as
if (#studentId = null)
select * from Student
else
select * from Student where studentId = #studentId
Output
exec usp_GetStudents -- No records returned though there are records in the table
exec usp_GetStudents #studentId = null -- No records returned
exec usp_GetStudents #studentId = 256 -- 1 entry returned
Just curious to know if anything is wrong in the syntax/logic for returning all the entries of the table?
Thank you

You're trying to test for null using =, a comparison operator. If you're using ANSI nulls, any comparison against null is false.
Where #studentId is any value (or null) the following expressions are all false:
#studentId = null -- false
#studentId > null -- false
#studentId >= null -- false
#studentId < null -- false
#studentId <= null -- false
#studentId <> null -- false
So, in order to test for null you must use a special predicate, is null, i.e.:
#studentId is null

Shorter way to do that:
create procedure usp_GetStudents #studentId int = null
as
select * from Student
where studentId = isnull(#studentId,studentId)
You can't chack if value is null using =.
For your example you have to replace condition #studentId = null to is null syntax.
Try to change your code as below:
create procedure usp_GetStudents #studentId int = null
as
if (#studentId is null)
select * from Student
else
select * from Student where studentId = #studentId

Change the = to an is
create procedure usp_GetStudents #studentId int = null
as
if (#studentId is null)
select * from Student
else
select * from Student where studentId = #studentId

Related

T-SQL Query null issue

I want to write a stored procedure which will return true or false based on the following condition:
if 2 columns in all the rows in a result set are null, then return true
if at least one is not null, then return false
Like for example in the following query
select *
from Products
where productID = 123
and ProductType is null
and ProductDate is null
The above query can return 1 row or return 100 rows, so if all are null, then it will return true; if at least 1 is not null, then it will return false.
Given this sample data and desired results:
CREATE TABLE dbo.Products
(
ProductID int,
ProductType int,
ProductDate date
);
INSERT dbo.Products(ProductID, ProductType, ProductDate)
VALUES
(123, NULL, NULL),(123, NULL, NULL), -- this should return 1
(456, 5, NULL), (456, NULL, NULL), -- this should return 0
(789, NULL, GETDATE()); -- this should return 0
We can do:
CREATE PROCEDURE dbo.whatever1
#ProductID int
AS
BEGIN
DECLARE #return int = CASE WHEN EXISTS (SELECT 1
FROM dbo.Products
WHERE productID = #ProductID
AND (ProductType IS NOT NULL OR ProductDate IS NOT NULL)
) THEN 0 ELSE 1 END;
RETURN #return;
END
Then to execute:
DECLARE #hr int;
EXEC #hr = dbo.whatever1 #ProductID = 123;
SELECT #hr;
EXEC #hr = dbo.whatever1 #ProductID = 456;
SELECT #hr;
EXEC #hr = dbo.whatever1 #ProductID = 789;
SELECT #hr;
Results:
1
0
0
The tricky bit is when you say "return" we're not quite sure if you mean the explicit RETURN keyword, or an OUTPUT parameter, or a resultset. I feel like an OUTPUT parameter would be more appropriate in this case, since RETURN is generally reserved for error/status (and can only be an int). So we can use a more appropriate type depending on what we need:
CREATE PROCEDURE dbo.whatever2
#ProductID int,
#return bit OUTPUT
AS
BEGIN
SET #return = CASE WHEN EXISTS (SELECT 1
FROM dbo.Products
WHERE productID = #ProductID
AND (ProductType IS NOT NULL OR ProductDate IS NOT NULL)
) THEN 0 ELSE 1 END;
END
Then to execute:
DECLARE #return bit;
EXEC dbo.whatever2 #ProductID = 123, #return = #return OUTPUT;
SELECT #return;
EXEC dbo.whatever2 #ProductID = 456, #return = #return OUTPUT;
SELECT #return;
EXEC dbo.whatever2 #ProductID = 789, #return = #return OUTPUT;
SELECT #return;
Results:
1
0
0
Working example here (though be aware that fiddle displays bit as true/false instead of 1/0): db<>fiddle
If either of these procedures don't work like you expect, please adjust the fiddle with your sample data that you think is producing wrong results, and please be specific about what you mean by "return" and "always give me a value of" - we have no idea where you are seeing 1 and why you would expect to see 0.
Try:
IF EXISTS (SELECT 1 FROM Products WHERE ProductID = 123 AND (ProductType IS NOT NULL OR ProductDate IS NOT NULL))
SELECT CAST (0 AS bit)
ELSE
SELECT CAST (1 AS bit)
Using exists is more efficient than a count since it will only scan the table until the condition is met. Count will always scan the whole table.
select IIF(COUNT(*) = 0, 'true','false')
from Products
where productID=#ProductID and ProductType is null and ProductDate is null

Stored procedure in SQL Server with select query then if condition and then insert query

I want to create a stored procedure in which first select statement and depending on the selected parameter if valid, insert record in the another table else do nothing. How to write the stored procedure for this?
I have tried with stored procedure and it is executed with no errors, but when I tried to EXEC stored procedure, it doesn't do the task as written in procedure.
CREATE PROCEDURE sp_CreateExpiryDocumentFollowup
(#param INT = NULL,
#param2 INT = NULL,
#param3 INT = 1,
#param4 BIT = 0,
#followupid INT = NULL)
AS
BEGIN
SELECT
#param1 = [TABLE_A].[VEHICLE_ID],
#param2 = [TABLE_A].[VEHICLE_DOCUMENT_ID],
#followupid = [TABLE_B].[FOLLOWUP_ID]
FROM
[TABLE_A]
LEFT JOIN
[TABLE_B] ON [TABLE_B].[VEHICLE_DOCUMENT_ID] != [TABLE_A].[VEHICLE_DOCUMENT_ID]
WHERE
[TABLE_A].[STATUS] = 1;
IF #followupid = NULL
BEGIN
INSERT INTO [TABLE_B] (VALUE_1, VALUE_2, VALUE_3, VALUE_4)
VALUES (#param1, #param2, #param3, #param4)
END
END
GO
I expect the record to insert in the TABLE_B if #followupid is null. The #followupid is null as I executed the select statement only. But while executing whole stored procedure it will return the result 0 row which in my case should be 1. And I checked the table as well, no any record is inserted but stored procedure runs successfully.
Here's how I would write it:
CREATE PROCEDURE sp_CreateExpiryDocumentFollowup
(
--#param1 int = null -- this is no longer needed
--#param2 int = null -- this is no longer needed
#param3 int = 1,
#param4 bit = 0
--,#followupid int = null -- -- this is no longer needed
)
AS BEGIN
INSERT INTO [TABLE_B] (VALUE_1, VALUE_2, VALUE_3, VALUE_4)
SELECT [TABLE_A].[VEHICLE_ID], [TABLE_A].[VEHICLE_DOCUMENT_ID], #param3, #param4
FROM [TABLE_A]
LEFT JOIN [TABLE_B]
-- as stated in the comments to the questions,
-- Shouldn't this condition be `=` instead of `!=`?
ON [TABLE_B].[VEHICLE_DOCUMENT_ID] != [TABLE_A].[VEHICLE_DOCUMENT_ID]
WHERE [TABLE_A].[STATUS] = 1
AND [TABLE_B].[FOLLOWUP_ID] IS NULL
END
GO
Instead of first selecting and then inserting, you can do an insert...select operation which leads to a shorter, more readable code.
This way, if the select statement doesn't return any rows, nothing gets inserted into the target table.
Also, you can't use equality operators on NULL in SQL. Instead, you can only use IS NULL or IS NOT NULL (Or NOT IS NULL if you like that better).
Please note that if the select statement returns more than one row, all of them will be inserted into the target table.

SQL Server - Using CASE statement

I have a SELECT statement with a WHERE clause that I want to dynamically change depending if a parameter is supplied or not.
I can't seem to understand how to use CASE statement in a WHERE clause but this is how I want it to look like using an IF statement.
DECLARE #Gender NVARCHAR(100) = NULL --this is an INPUT parameter and may or may not be NULL
DECLARE #Status NVARCHAR(100) = NULL --this is an INPUT parameter and may or may not be NULL
SELECT Name
FROM Person
WHERE
-- first WHERE clause
IF #Gender IS NULL
BEGIN
Gender IS NULL
END
ELSE
BEGIN
Gender = #Gender
END
AND
-- second WHERE clause
IF #Status IS NULL
BEGIN
Status IS NULL
END
ELSE
BEGIN
Status LIKE '%' + #Status + '%'
END
Is it possible to transform this code into a CASE statement?
I think you want:
select p.name
from person p
where ( (#gender is null and gender is null) or gender = #gender) and
( (#status is null and status is null) or status = #status);
Note that this does "null-matching". Often, people want to use NULL to select all records, not just the NULL ones. If that is what you intend, then:
select p.name
from person p
where ( #gender is null or gender = #gender) and
( #status is null or status = #status);
In either situation, case is not needed in the where. As a general rule, don't use case in where -- unless you really need it to control the order of evaluation of expressions.
You can do this:
SELECT Name
FROM Person
WHERE Gender = COALESCE(#gender, Gender)
AND (#Status is null or Status like '%' + #status + '%')
DECLARE #Gender NVARCHAR(100) = NULL --this is an INPUT parameter and may or may not be NULL
DECLARE #Status NVARCHAR(100) = NULL --this is an INPUT parameter and may or may not be NULL
SELECT Name
FROM Person
WHERE CASE WHEN #Gender IS NULL THEN 1
WHEN #Gender = ISNULL(Gender, '') THEN 1
ELSE 0
END = 1
AND CASE WHEN #Status IS NULL THEN 1
WHEN ISNULL(Status, '') LIKE '%' + #Status + '%' THEN 1
ELSE 0
END = 1

SQL OR CONDITION ON TWO DIFFERENT COLUMNS

I have two fields (#EmployeeId,#SSOId) out of which one value can come or both can come, but when i am applying OR condition it is not giving me correct output. What i am doing wrong ?
ALTER PROCEDURE [dbo].[usp_User_GetDetails] (
#UserId INT = NULL
,#ADSId NVARCHAR(32) = NULL
,#EmployeeId NVARCHAR(32) = NULL
,#SSOId NVARCHAR(32) = NULL
,#UserName NVARCHAR(100) = NULL
)
AS
*/
SET NOCOUNT ON;
BEGIN
SELECT [USER_ID] AS UserId
,[FIRST_NM] AS FirstName
,[LST_NM] AS LastName
,[FULL_NM] AS FullName
,[ADS_USER_ID] AS ADSId
,[SEG_ID] AS SegmentId
,[PHONE_NO] AS PhoneNo
,[FAX_NO] AS FaxNo
,[EMP_ID] AS EmployeeId
,[EMAIL_AD_TX] AS Email
,[SSO_ID] AS SSOId
,[SFDC_IN] AS IsSFDC
,[USER_SFDC_ID] AS UserSFDCId
,[MGR_SFDC_ID] AS ManagerSFDCId
,[ACT_IN] AS IsActive
,[SYS_USER_IN] AS IsSystemUser
,[PORFOLIO_OWN_IN] AS CanHavePortfolio
,[MGR_ID] AS ManagerId
,[LST_LOG_IN_TS] AS LastLoginDate
,[EMP_BAND_TX] AS Band
,[CREAT_TS] AS CreatedDate
,[CREAT_BY_USER_ID] AS CreatedBy
,[LST_UPDT_TS] AS UpdatedDate
,[LST_UPDT_BY_USER_ID] AS UpdatedBy
FROM [dbo].[USER] WITH (NOLOCK)
WHERE ([EMP_ID] = ISNULL(#EmployeeId, [EMP_ID])OR [SSO_ID] = ISNULL(#SSOId, [SSO_ID])
AND [ADS_USER_ID] = ISNULL(#ADSId, [ADS_USER_ID])
AND [USER_ID] = ISNULL(#UserId, [USER_ID])
AND [FULL_NM] LIKE CASE
WHEN #UserName IS NOT NULL
THEN '%' + #UserName + '%'
ELSE [FULL_NM]
END
END
I don't think the parentheses are balanced correctly. In any case, I would write this without the ISNULL():
WHERE ((#EmployeeId IS NULL OR EMP_ID = #EmployeeId) OR
(#SSOId IS NULL OR SSO_ID = #SSOId)
) AND
(#ADSId IS NULL OR ADS_USER_ID = #ADSId) AND
(#UserId IS NULL OR USER_ID = #UserId) AND
(#UserName IS NULL OR FULL_NM LIKE '%' + #UserName + '%')
I am guessing that the OR is for the first two conditions. This is where the parens don't seem to line up in the query in the question.
I prefer this construct for two reasons. First, it handles NULL values in the column values as well as the parameter values. And second -- because it is more general -- it is one of the standard two ways I use to handle optional parameters (the other is to use dynamic SQL which can make use of indexes).
Query seems to be okay .Are you passing DBNull from you C# code or empty text
WHERE (#EmployeeId IS NULL OR (EMP_ID = #EmployeeId))
AND (#SSOId IS NULL OR (SSO_ID = #SSOId))
AND [ADS_USER_ID] = ISNULL(#ADSId, [ADS_USER_ID])
AND [USER_ID] = ISNULL(#UserId, [USER_ID])
AND [FULL_NM] LIKE CASE
WHEN #UserName IS NOT NULL
THEN '%' + #UserName + '%'
ELSE [FULL_NM]
Used this script
WHERE EMP_ID = CASE WHEN ISNULL(#EmployeeId,0) > 0 THEN #EmployeeId ELSE EMP_ID END AND SSO_ID = CASE WHEN ISNULL(#SSOId,0) > 0 THEN #SSOId ELSE SSO_ID END

How to condition my select based on boolean parameter

I'm creating a stored procedure that should return a list of users based on a boolean flag #status as follows:
If status is not informed, the query must return all users
If 'status' is passed as true, the query must return only users where the column LastAccess is not null.
If status is passed as false, the query must return users which column LastAccess is null.
This column is of type datetime.
I'm ok dealing the first use case, with #status is null in the last line of the following code:
CREATE PROCEDURE selectUsersByStatus (
#userId int,
#status bit
)
AS
SELECT * from users
WHERE users.id = #userId
and (#status is null or CASE #status WHEN 1 THEN (users.LastAccess is not null) ELSE (users.LastAccess is null))
However the rest of the line obviously doesn't work. How to proceed?
Thanks
Try simple:
CREATE PROCEDURE selectUsersByStatus (
#userId int,
#status bit
)
AS
SELECT * from users
WHERE users.id = #userId
and (#status is null OR (#status = 1 AND users.LastAccess IS NOT NULL) OR (#status = 0 AND users.LastAccess IS NULL))