I have a stored procedure for sql server 2008 like this:
create procedure test_proc
#someval int,
#id int
as
update some_table
set some_column = ISNULL(#someval, some_column)
where id = #id
go
If the parameter #someval is NULL, this SP will just use the existing value in some_column.
Now I want to change this behaviour such that if value for #someval is 0, a NULL is stored in some_column otherwise it behave just the way it is doing now.
So I am looking for something like:
if #someval == 0
set some_column = NULL
else
set some_column = ISNULL(#someval, some_column)
I don't have the option to create a varchar #sql variable and call sq_executesql on it (at least that is the last thing I want to do). Any suggestions on how to go about doing this?
You can do this using the CASE expression. Something like this:
update some_table
set some_column = CASE WHEN #someval = 0 THEN NULL
WHEN #someval IS NULL THEN somcolumn
ELSE #someval -- the default is null if you didn't
-- specified one
END
where id = #id
something like this?
create procedure test_proc
#someval int,
#id int
as
update some_table
set some_column = CASE
WHEN #someval = 0 THEN NULL
ELSE ISNULL(#someval, some_column) END
where id = #id
go
I think it's a really bad idea - I'd suggest that if someone wants to store a NULL, they really shouldn't have to pass some other magical value to cause it to happen. However, let's show how it can be done:
update some_table
set some_column = CASE WHEN #someVal = 0 THEN NULL ELSE ISNULL(#someval, some_column) END
where id = #id
Given the simplicity of the stored procedure in your question, of course, the whole matter can be cleared up by not calling the stored procedure if you don't want to alter some_column. I'd imagine that your real procedure is more complex. Instead, what I'd do is have:
create procedure test_proc
#someval int,
#someval_specified bit,
#id int
as
update some_table
set some_column = CASE WHEN #someval_specified = 1 THEN #someval ELSE some_column END
where id = #id
And now NULL means NULL, 0 means 0, etc.
Related
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.
I have to write a query for a login page where the user can enter either emailid(varchar datatype) or userid(int datatype). How do I write a query for this without knowing the datatype of the input?
CREATE PROCEDURE my_stored_procedure
#emailId nvarchar(50) = NULL,
#userId int = NULL
AS
SET NOCOUNT ON;
IF (#emailId IS NOT NULL) AND (LEN(#emailId) > 0)
SELECT * FROM MYTABLE WHERE EmailId = #emailId
ELSE
SELECT * FROM MYTABLE WHERE UserId = #UserId
GO
Your input parameter should be of varchar type.
Then in your procedure use ISNUMERIC function to check for number. Have two queries based on that with IF statement in case if it is int or varchar.
DECLARE #userInput VARCHAR(50)
IF ISNUMERIC(#userInput) = 1
BEGIN
-- Your query with userid
END
ELSE
BEGIN
-- Your query with emailid
END
Try this query it may help you.
DECLARE #emailid varchar(250),#userid bigint
set #emailid='A'
SET #userid=3
select * from yourtable
where case when #emailid='A' THEN 'A' ELSE emailid END=#emailid
AND case when #userid=0 THEN 0 ELSE userid END=#userid
Note: You should pass 'A' as default value for email_id and 0 for userid or else you can pass your required input
Recently I have had to do a few variable updates to a table, and although I am aware of the MERGE statement (although need to catch up on all of that!), I also performed the following statement to optional update a table and wish to check if this is "a good idea" or has some hidden consequences that I not aware of.
So in my case, I pass a primary key to a table, however depending on if parameters passed are null or not, I update the column.. obviously if you had to ensure a forceful update (of a status etc.) then you would just update the column.. this is to save having multiple "IF / THEN" type structures..
create procedure sp_myprocedure
as
#id bigint,
#field1 int = null,
#field2 varchar(255) = null,
#field3 char(1) = null
begin
update my_table
set
field1 = case when #field1 is not null then #field1 else field1 end,
field2 = case when #field2 is not null then #field2 else field2 end,
field3 = case when #field3 is not null then #field3 else field3 end,
where
id = #id
end
Just after some thoughts of the above or is it best to pursue the MERGE statement for scenarios like the above?
Many thanks in advance,
This is fine although it can be written in a cleaner way.
update my_table
set
field1 = coalesce (#field1,field1)
,field2 = coalesce (#field2,field2)
,field3 = coalesce (#field3,field3)
where
id = #id and coalesce(#field1,#field2,#field3) is not null
You can also move the coalesce(#field1,#field2,#field3) is not null to a wrapper block
if coalesce(#field1,#field2,#field3) is not null
begin
update my_table
set
field1 = coalesce (#field1,field1)
,field2 = coalesce (#field2,field2)
,field3 = coalesce (#field3,field3)
where
id = #id
end
MERGE statement is not relevant here.
With MERGE the decision is if to INSERT, UPDATE or DELETE a record base on the non-existent/existent of a record with the same merge keys in the source/target table.
In your case it is always UPDATE.
create procedure sp_myprocedure
#id bigint,
#field1 int = null,
#field2 varchar(255) = null,
#field3 char(1) = null
as
begin
IF coalesce(#field1,#field2,#field3) is not null
update dbo.my_table
set
field1 = coalesce (#field1,field1),
field2 = coalesce (#field2,field2),
field3 = coalesce (#field3,field3)
where id = #id
END
Responding to the answer by #Dudu Markovitz:
MERGE statement is not relevant here.
I disagree , I think MERGE is entirely relevant here.
The idea is to create a source table expression using the parameter values with which to update the target table:
MERGE my_table T
USING ( VALUES ( #id, #field1, #field2, #field3 ) )
AS S ( id, field1, field2, field3 )
ON T.id = S.id
WHEN MATCHED THEN
UPDATE
SET field1 = COALESCE( S.field1, T.field1 ),
field2 = COALESCE( S.field2, T.field2 ),
field3 = COALESCE( S.field3, T.field3 );
Of course, if there was a single table-valued parameter (as arguable there should be) then the relevance of MERGE is even more obvious.
In the below procedure i may pass #Type value only sometimes otherwise it will be NULL.
How can i handle querying all data when NULL is passed and specific records when i pass a type. I don't want to build dynamic query for this simple handling.
CREATE PROCEDURE [dbo].[GetPatientImages]
(
#TenantId BIGINT,
#PatientId BIGINT,
#Type NVARCHAR(100)
)
AS
BEGIN
SET NOCOUNT ON
IF #TenantId IS NULL
RAISERROR('The value for #TenantID should not be null', 15, 1) -- with log
ELSE
BEGIN
SELECT
P.[DisplayFileName],
P.[StoredFileName],
P.[Location],
P.[Type],
P.[Description]
FROM
PatientImage P
WHERE P.PatientId=#PatientId AND P.TenantId=#TenantId AND P.[Type]=#Type
ORDER BY P.[Type] DESC
END
END
Try this:
WHERE P.PatientId=#PatientId AND P.TenantId=#TenantId AND
(#Type is null or P.[Type]=#Type)
Below is a stored procedure to check if there is a duplicate entry in the database based upon checking all the fields individually (don't ask why I should do this, it just has to be this way).
It sounds perfectly straightforward but the SP fails.
The problem is that some parameters passed into the SP may have a null value and therefore the sql should read "is null" rather than "= null".
I have tried isnull(),case statements,coalesce() and dynamic sql with exec() and sp_executesql and failed to implement any of these. Here is the code...
CREATE PROCEDURE sp_myDuplicateCheck
#userId int,
#noteType char(1),
#aCode char(3),
#bCode char(3),
#cCode char(3),
#outDuplicateFound int OUT
AS
BEGIN
SET #outDuplicateFound = (SELECT Top 1 id FROM codeTable
WHERE userId = #userId
AND noteType = #noteType
AND aCode = #aCode
AND bCode = #bCode
AND cCode = #cCode
)
-- Now set the duplicate output flag to a 1 or a 0
IF (#outDuplicateFound IS NULL) OR (#outDuplicateFound = '') OR (#outDuplicateFound = 0)
SET #outDuplicateFound = 0
ELSE
SET #outDuplicateFound = 1
END
I think you need something like this for each possibly-null parameter:
AND (aCode = #aCode OR (aCode IS NULL AND #aCode IS NULL))
If I understand your question correctly, then I encourage you to do a little research on:
SET ANSI_NULLS OFF
If you use this command in your stored procedure, then you can use = NULL in your comparison. Take a look at the following example code to see how this works.
Declare #Temp Table(Data Int)
Insert Into #Temp Values(1)
Insert Into #Temp Values(NULL)
-- No rows from the following query
select * From #Temp Where Data = NULL
SET ANSI_NULLS OFF
-- This returns the rows where data is null
select * From #Temp Where Data = NULL
SET ANSI_NULLS ON
Whenever you SET ANSI_NULLS Off, it's a good practice to set it back to ON as soon as possible because this may affect other queries that you run later. All of the SET commands only affect the current session, but depending on your application, this could span multiple queries, which is why I suggest you turn ansi nulls back on immediately after this query.
I think this should work with COALESCE function. Try this:
CREATE PROCEDURE sp_myDuplicateCheck
#userId int,
#noteType char(1),
#aCode char(3),
#bCode char(3),
#cCode char(3),
#outDuplicateFound int OUT
AS
BEGIN
SET #outDuplicateFound = (SELECT Top 1 id FROM codeTable
WHERE userId = #userId
AND noteType = #noteType
AND COALESCE(aCode,'NUL') = COALESCE(#aCode,'NUL')
AND COALESCE(bCode,'NUL') = COALESCE(#bCode,'NUL')
AND COALESCE(cCode,'NUL') = COALESCE(#cCode,'NUL')
)
-- Now set the duplicate output flag to a 1 or a 0
IF (#outDuplicateFound IS NULL) OR (#outDuplicateFound = '') OR (#outDuplicateFound = 0)
SET #outDuplicateFound = 0
ELSE
SET #outDuplicateFound = 1
END
Good Luck!
Jason
Try this :
CREATE PROCEDURE sp_myDuplicateCheck
#userId int = 0,
#noteType char(1) = "",
#aCode char(3) = "",
#bCode char(3) = "",
#cCode char(3) = "",
#outDuplicateFound int OUT
AS
BEGIN
SET #outDuplicateFound = (SELECT Top 1 id FROM codeTable
WHERE #userId in (userId ,0)
AND #noteType in (noteType,"")
AND #aCode in (aCode , "")
AND #bCode in (bCode , "")
AND #cCode in (cCode ,"")
)
-- Now set the duplicate output flag to a 1 or a 0
IF (#outDuplicateFound IS NULL) OR (#outDuplicateFound = '') OR (#outDuplicateFound = 0)
SET #outDuplicateFound = 0
ELSE
SET #outDuplicateFound = 1
END
What this basically does is to provide default values to the input parameters in case of null and then in the where condition checks only if the values are not equal to the default values.
I would first add a check to see if all of the parameters were null at run time, i.e.,
IF(COALESCE(#userId, #noteType, #aCode, #bCode, #cCode) IS NULL)
BEGIN
-- do something here, log, print, return, etc.
END
Then after you've validated that the user passed something in you can use something like this in your WHERE clause
WHERE userId = COALESCE(#userId, userId)
AND noteType = COALESCE(#noteType, noteType)
AND aCode = COALESCE(#aCode, aCode)
AND bCode = COALESCE(#bCode, bCode)
AND cCode = COALESCE(#cCode, cCode)
EDIT: I may have missed the intent that if the parameter was passed in as null that means you explicitly want to test the column for null. My above where clause assumed that the null parameter meant 'skip the test on this column.'
Alternatively, I believe you can use your original query and add the ANSI_NULLS set option at the stored procedure create time. For example,
SET ANSI_NULLS OFF
GO
CREATE PROC sp_myDuplicateCheck....
Effectively this should allow your code to then evaluate column=null as opposed to column is null. I think Kalen Delaney once coined the ANSI_NULLS and QUOTED_IDENTIFIER options as 'sticky options' because if they're set at procedure create time they stay with the procedure at run time, regardless of how the connection at that time is set.
SET ANSI_NULLS OFF/On
That way you can do colName = null