Accepting NULLS into Stored Procedures - sql

I've got the below stored procedure that is very simple, but it allows NULL values to be passed in.
I'm just wondering what best practice is here, and how to deal with NULLs in cases such as this?
ALTER PROCEDURE [dbo].[spGetClient]
#ClientID int
AS
BEGIN
SET NOCOUNT ON;
SELECT
......
FROM
Client
WHERE
ClientID = #ClientID

You can raise a custom error:
ALTER PROCEDURE [dbo].[spGetClient]
#ClientID int
AS
BEGIN
SET NOCOUNT ON;
if #ClientID is null
BEGIN
raiserror('The value for ClientID should not be null', 15, 1)
return;
END
SELECT
......
FROM
Client
WHERE
ClientID = #ClientID
END

The simplest thing would be to just declare the argument as NOT NULL:
ALTER PROCEDURE [dbo].[spGetClient] (
#ClientID int NOT NULL
)
. . .
However, that is not quite allowed for all stored procedures.
Your stored procedure is fine. If a NULL value is passed in, then it will return no rows. This seems reasonable, because ClientId is probably never NULL in the Client table, so I don't see a problem.

Related

Why would a stored procedure return a 0 and the selected id?

I have a stored procedure like this:
CREATE PROCEDURE [dbo].[create_myNewId]
(#parentId BIGINT)
AS
BEGIN
SET NOCOUNT ON;
INSERT INTO [Mapping] (ParentId)
VALUES (#parentId)
SELECT SCOPE_IDENTITY();
END
This, when run on its own, returns the new id that has been assigned to the new row that's inserted with the parent id. However, when I do something like this:
DECLARE #NewId int
EXEC #NewId = create_myNewId #parentId = 33333
SELECT #NewId
When running this, the output window shows the result of the stored procedure, which returns an Id but #NewId always is 0. I fixed this by changing the stored procedure to use RETURN SCOPE_IDENTITY() but I was wondering why SELECT didn't work in this case?
I have my suspicions that it's something around the 0 being the success status being returned first from the stored procedure rather than the result, but was curious why this doesn't then happen when called directly from the client.
No! Write the procedure the right way:
CREATE PROCEDURE [dbo].[create_myNewId] (
#parentId bigint,
#outId bigint OUTPUT
) AS
BEGIN
SET NOCOUNT ON;
DECLARE #ids TABLE (id bigint);
INSERT INTO [Mapping](ParentId)
OUTPUT id INTO #ids
VALUES (#parentId);
SELECT #outId = id
FROM #ids;
END;
Then call this as:
DECLARE #NewId int;
EXEC create_myNewId #parentId = 33333, #NewId OUTPUT;
SELECT #NewId;
The OUTPUT clause is the recommend way to get results from a data-modification clause. The older methods using the *_IDENTITY() functions should be obsoleted.
Stored procedures do return values. These are integers that are designed to return status information. Other information should be returned via OUTPUT parameters.
Microsoft's design intent for stored procedures is that they always return an int to describe how successful the process undertaken by the procedure was. It's not intended to return a result data, and you're free to define the bits you want to return to describe succes, partial success etc. You could abuse it to return an integer result data (count query for example) if you wanted, but it's not the design intention
Executing a select query within a stored procedure creates a result set you can read on your client if the sproc is the kind that is intended to return data
My suggestion is to use an OUTPUT parameter. Not only will it be 'easier' to use when calling the stored procedure, it will also be clearer to the person calling the stored procedure.
CREATE PROCEDURE [dbo].[create_myNewId]
(#parentId BIGINT,
#myNewId BIGINT OUTPUT)
AS
BEGIN
SET NOCOUNT ON;
INSERT INTO [Mapping] ([ParentId])
VALUES (#parentId);
SET #myNewId = SCOPE_IDENTITY();
END;
GO
You would then call your stored procedure like this:
DECLARE #myNewId BIGINT;
EXECUTE [dbo].[create_myNewId] #parentId = 0, -- bigint
#myNewId = #myNewId OUTPUT; -- bigint
SELECT [This was just inserted] = #myNewId;
For anyone who has 0 as return value from a stored procedure, check if the stored procedure executes from the right database and only one procedure exists within the given context. Output parameters wouldn't be of any use if you ever plan to access the DB with ORM and the procedure returns an object's property.

I want to write the code I created with the 'Stored procedure' as a function

CREATE PROC add_person
(
#id tinyint,
#name nvarchar(max),
#surname nvarchar(max),
#salary int,
#job nvarchar(max)
)
AS
BEGIN
INSERT INTO information
VALUES(#id,#name,#surname,#salary,#job)
END
I want to write this code as a function. But the concept of "return" confuses me. That's why I couldn't.
I tried to write the code above as a function. This code came out.
CREATE FUNCTION add_person
(
#id tinyint,
#name nvarchar(max),
#surname nvarchar(max),
#salary int,
#job nvarchar(max)
)
RETURNS TABLE
AS
BEGIN
RETURN INSERT INTO information -- not work
VALUES(#id,#name,#surname,#salary,#job)
END
If you want to return the newly created table, you can use the stored procedure to do that. If you're using SQL Server, the code would be:
BEGIN
INSERT INTO information -- not work
VALUES(#id,#name,#surname,#salary,#job);
SELECT * FROM information WHERE id = ##identity; -- this is the primary key just created.
END
Functions are much more limited in their functionality than are stored procedures.
Although insert is allowed, it is only allowed in local variables. As the documentation says:
INSERT, UPDATE, and DELETE statements modifying local table variables.
On the other hand, a stored procedure can return a value. Normally, this is a status code, where 0 means everything succeeded, and any other value means that the process failed.

Execute a stored procedure from another, and return two variables

I have a stored procedure called clients with 3 parameters: the first one for user input, and the last two are OUTPUT parameters.
This is the code:
CREATE PROCEDURE clients
(#name NVARCHAR(100),
#id_client int OUTPUT,
#messg varchar(1) OUTPUT)
AS
BEGIN
SET NOCOUNT ON;
BEGIN TRY
BEGIN TRAN
IF NOT EXISTS (SELECT name FROM client WHERE name = #name)
BEGIN
INSERT INTO client(name) VALUES (#name);
SET #id_client = SCOPE_IDENTITY();
SET #messg = 'o'
COMMIT TRAN
END
ELSE
BEGIN
SELECT #id_client = id_client
FROM client
WHERE name = #name;
SET #messg = 'o'
COMMIT TRAN
END
END TRY
BEGIN CATCH
SET #messg = 'e'
ROLLBACK TRAN
END CATCH
END
I need to call this stored procedure from another the second one is called updateS, and I'm trying the following:
CREATE PROCEDURE updateS
(#clientname VARCHAR(100))
AS
BEGIN
SET NOCOUNT ON;
DECLARE #id INT;
DECLARE #msg VARCHAR(1);
EXEC clients #clientname, #id, #msg; --Problem here to retrieve the id
END
This stored procedure has a parameter for the name of the client, but I need to retrieve the id of the client, but it doesn't work as I'm trying.
Basically I need to get the id and use it in the second stored procedure.
Any question post on comments.
You need to specify OUTPUT when you execute the stored procedure as well as when you define it:
EXEC clients #clientname, #id OUTPUT, #msg OUTPUT;
Did you miss the output keyword while passing the parameter to the stored procedure? Moreover, I would change the clients stored procedure and make the parameter as char(1) rather like #messg char(1) OUTPUT

In SQL Server, testing a uniqueidentifier for null doesn't seem to work

Not sure if this is the best approach, but I have a stored procedure with an OUTPUT parameter as follows;
create procedure [dbo].[sp_get_site_idx]
#site_name varchar(100),
#result uniqueidentifier output
as
begin
select #result = [primary_idx_col] from [site] where upper([site].[site_name]) = upper(#site_name);
if (#result is null)
begin
< insert a new row>
< run the above select statement again>
end;
end;
When a #site_name that I know does not exist is supplied, the condition (#result is null) is never true, in fact #result appears to be undefined (similar to when there's an exception in a programming language).
Table [site] was created as:
create table [site] (
[primary_idx_col] UNIQUEIDENTIFIER DEFAULT NEWID() constraint pk_site_pk primary key,
...
);
Strangely, if I slightly modify the select statement to:
select #result = [primary_idx_col] from [site] where upper([site].[site_name]) = upper(#site_name) group by [primary_idx_col];
then (#result is null) will evaluate to true.
Please could you explain this behaviour? What is worng with the first select statement?
Thanks in advance.
UNIQUEIDENTIFIER can be checked against NULL.
I tried putting your code into a test database, and the logic seems to be working for me.
If I call your Stored Procedure with:
DECLARE #RESULT2 UNIQUEIDENTIFIER;
EXEC dbo.SP_GET_SITE_IDX #SITE_NAME = '<INSERT VALUE HERE>', -- varchar(100)
#RESULT = #RESULT2 OUTPUT;-- uniqueidentifier
SELECT #RESULT2
then I get the proper result depending on whether the site name is in the table or not.
Does it not insert the row in your IF, in the procedure?
Are you sure the site in question is not in your table?
It is possible that the site is in your table, but with a NULL key/value?

Store the result of a stored procedure without using an output parameter

I have 2 stored procedures: up_proc1 and up_proc2.
This is (a simplified version of) up_proc2:
CREATE PROCEDURE dbo.up_proc2
#id_campaign uniqueidentifier, #id_subcampaign uniqueidentifier,
#id_lead uniqueidentifier, #offer NVARCHAR(1000) = NULL
AS
SET NOCOUNT ON
DECLARE #id UNIQUEIDENTIFIER
SELECT #id = id FROM prospects WHERE id_lead = #id_lead
AND id_campaign = #id_campaign AND id_subcampaign = #id_subcampaign
IF #id IS NULL
BEGIN
SET #id = newid ()
INSERT INTO prospects (id, id_campaign, id_subcampaign, id_lead, offer)
values (#id, #id_campaign, #id_subcampaign, #id_lead, #offer)
END
ELSE
BEGIN
UPDATE prospects set offer = #offer WHERE id=#id
END
SELECT #id AS ID
GO
From up_proc1 I call up_proc2. What I would like to achieve is to store the #id of up_proc2 in a variable declared in up_proc1. Is this possible without using an output parameter?
This is how up_proc1 looks like:
CREATE PROCEDURE dbo.up_proc1
AS
SET NOCOUNT ON
DECLARE #fromProc2 UNIQUEIDENTIFIER
-- NOT WORKING
-- select #fromProc2 = exec up_insertProspects [snip]
-- ALSO NOT WORKING
-- exec #fromProc2 = up_insertProspects [snip]
What you could do is store the output into a table variable:
DECLARE #tmpTable TABLE (ID UNIQUEIDENTIFIER)
INSERT INTO #tmpTable
EXEC dbo.up_proc2 ..........
and then go from there and use that table variable later on.
You can certainly consume this as an output parameter in proc2 without affecting how your C# code retrieves the eventual resultset.
ALTER PROCEDURE dbo.up_proc2
#id_campaign uniqueidentifier,
#id_subcampaign uniqueidentifier,
#id_lead uniqueidentifier,
#offer NVARCHAR(1000) = NULL,
#fromProc2 UNIQUEIDENTIFER = NULL OUTPUT
AS
BEGIN
SET NOCOUNT ON;
...
C# can ignore the new parameter since it is nullable (but since a single output parameter is more efficient than a data reader, you may consider updating your C# code to take advantage of the output parameter later).
Now in proc1:
ALTER PROCEDURE dbo.up_proc1
AS
BEGIN
SET NOCOUNT ON;
DECLARE #fromProc2 UNIQUEIDENTIFIER;
EXEC dbo.up_proc2
--... other parameters ...,
#fromProc2 = #fromProc2 OUTPUT;
-- now you can use #fromProc2
END
GO