Scope_Identity not being returned as expected - sql

Using entity Framework, vb and sql server.
I have a stored procedure (shown below) to insert a new record and return the new identity of the record concerned (the id column is a properly defined identity column). The stored procedure is imported into my entity data model and called as a function, with the expected result being the identity of the new record. However I always get 0 returned instead.
If I execute the stored procedure in ssms I get the result I'm expecting in the results window and also a separate result with the value of 0 (which is clearly what is being returned to my mapped stored procedure).
Either I have an error in my stored procedure, or I'm doing something wrong with the way that I call the function (also shown below). I'd be really grateful for any suggestions that you might have to offer.
The stored procedure:
ALTER PROCEDURE [Finances].[CreatePurchaseInvoicePayment]
-- Add the parameters for the stored procedure here
#PurchaseInvoiceId int = NULL,
#SupplierId int = NULL,
#PurchaseInvoicePaymentDate date = NULL,
#Amount money = NULL,
#PaymentType int = NULL,
#ChequeNumber nchar(10) = NULL,
#BankAccountId int = NULL,
#purchaseInvoiceCreditNoteId int = NULL,
#ConversionFactor numeric(4,2) = NULL,
#ModifiedDate date = NULL,
#id int = NULL OUTPUT
AS
BEGIN
-- SET NOCOUNT ON added to prevent extra result sets from
-- interfering with SELECT statements.
SET NOCOUNT ON;
-- Insert statements for procedure here
INSERT INTO Finances.PurchaseInvoicePayments
VALUES
(
#PurchaseInvoiceId,
#SupplierId,
#PurchaseInvoicePaymentDate,
#Amount,
#PaymentType,
#ChequeNumber,
#BankAccountId,
#purchaseInvoiceCreditNoteId,
#ConversionFactor,
#ModifiedDate
)
SET #id = SCOPE_IDENTITY()
END
which is being called like this:
Try
pipId = CInt(te.CreatePurchaseInvoicePayment(pip.PurchaseInvoiceId, pip.SupplierId, pip.PurchaseInvoicePaymentDate, pip.Amount, pip.PaymentType, pip.ChequeNumber, pip.BankAccountId, pip.PurchaseInvoiceCreditNoteId, pip.ConversionFactor, pip.ModifiedDate,nothing).FirstOrDefault)
Catch ex As Exception
As I say if anyone could point me in the right direction I'd be most grateful.

I believe function expects an actual resultset from the SP. In this case you don't need an output parameter, just add at the end of SP:
SELECT SCOPE_IDENTITY()
and the function should read it correctly.

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.

Why is the default value in the output parameter of this stored procedure not returning? [duplicate]

I have a stored procedure that has a bunch of input and output parameters because it is Inserting values to multiple tables. In some cases the stored proc only inserts to a single table (depending on the input parameters). Here is a mocked up scenario to illustrate.
Tables / Data Objects:
Person
Id
Name
Address
Name
Id
FirstName
LastName
Address
Id
Country
City
Say I have a stored procedure that inserts a person. If the address doesn't exist I won't add it to the Address table in the database.
Thus when I generate the code to call the stored procedure I don't want to bother adding the Address parameter. For INPUT parameters this is ok because SQL Server allows me to supply default values. But for the OUTPUT parameter what do I do in the stored procedure to make it optional so I do not receive an error...
Procedure or function 'Person_InsertPerson' expects parameter
'#AddressId', which was not supplied.
Both input and output parameters can be assigned defaults. In this example:
CREATE PROCEDURE MyTest
#Data1 int
,#Data2 int = 0
,#Data3 int = null output
AS
PRINT #Data1
PRINT #Data2
PRINT isnull(#Data3, -1)
SET #Data3 = #Data3 + 1
RETURN 0
the first paramter is required, and the second and third are optional--if not set by the calling routine, they will be assigned the default values. Try messing around with it and the following test-call routine in SSMS using different values and settings to see how it all works together.
DECLARE #Output int
SET #Output = 3
EXECUTE MyTest
#Data1 = 1
,#Data2 = 2
,#Data3 = #Output output
PRINT '---------'
PRINT #Output
Output parameters and default values do not work well together! This is from SQL 10.50.1617 (2008 R2). Do not be fooled into believing this construct magically does a SET to that value on your behalf (like my co-worker did)!
This "toy" SP interrogates the OUTPUT parameter value, whether it is the default value or NULL.
CREATE PROCEDURE [dbo].[omgwtf] (#Qty INT, #QtyRetrieved INT = 0 OUTPUT)
AS
IF #QtyRetrieved = 0
BEGIN
print 'yay its zero'
END
IF #QtyRetrieved is null
BEGIN
print 'wtf its NULL'
END
RETURN
If you send in an uninitialized value (i.e. NULL) for the OUTPUT, you really got NULL inside the SP, and not 0. Makes sense, something got passed for that parameter.
declare #QR int
exec [dbo].[omgwtf] 1, #QR output
print '#QR=' + coalesce(convert(varchar, #QR),'NULL')
output is:
wtf its NULL
#QR=NULL
If we add an explicit SET from the caller we get:
declare #QR int
set #QR = 999
exec [dbo].[omgwtf] 1, #QR output
print '#QR=' + coalesce(convert(varchar, #QR),'NULL')
and the (unsurprising) output:
#QR=999
Again, makes sense, a parameter is passed, and SP took no explicit action to SET a value.
Add a SET of the OUTPUT parameter in the SP (like you're supposed to do), but do not set anything from the caller:
ALTER PROCEDURE [dbo].[omgwtf] (#Qty INT, #QtyRetrieved INT = 0 OUTPUT)
AS
IF #QtyRetrieved = 0
BEGIN
print 'yay its zero'
END
IF #QtyRetrieved is null
BEGIN
print 'wtf its NULL'
END
SET #QtyRetrieved = #Qty
RETURN
Now when executed:
declare #QR int
exec [dbo].[omgwtf] 1234, #QR output
print '#QR=' + coalesce(convert(varchar, #QR),'NULL')
the output is:
wtf its NULL
#QR=1234
This is the "standard" behavior for OUTPUT parameter handling in SPs.
Now for the plot twist: The only way to get the default value to "activate", is to not pass the OUTPUT parameter at all, which IMHO makes little sense: since it's set up as an OUTPUT parameter, that would mean returning something "important" that should be collected.
declare #QR int
exec [dbo].[omgwtf] 1
print '#QR=' + coalesce(convert(varchar, #QR),'NULL')
gives this output:
yay its zero
#QR=NULL
But this fails to capture the SPs output, presumably the purpose of that SP to begin with.
IMHO this feature combination is a dubious construct I would consider a code smell (phew!!)
Looks like I can just add a default value to the OUTPUT parameter such as:
#AddressId int = -1 Output
Seems like its poor in terms of readability since AddressId is intended strictly as an OUTPUT variable. But it works. Please let me know if you have a better solution.
Adding on to what Philip said:
I had a stored procedure in my sql server database that looked like the following:
dbo.<storedProcedure>
(#current_user char(8) = NULL,
#current_phase char(3) OUTPUT)
And I was calling it from my .net code as the following:
DataTable dt = SqlClient.ExecuteDataTable(<connectionString>, <storedProcedure>);
I was getting an System.Data.SqlClient.SqlException: Procedure or function expects parameter '#current_phase', which was not supplied.
I am also using this function somewhere else in my program and passing in a parameter and handling the output one. So that I didn't have to modify the current call I was making I just changed the stored procedure to make the output parameter also optional.
So it now looks as the following:
dbo.<storedProcedure>
(#current_user char(8) = NULL,
#current_phase char(3) = NULL OUTPUT)
Since you are executing a stored procedure and not a SQL statement, you have to set the command type of your SQL Command to Stored Procedure:
cmd.CommandType = CommandType.StoredProcedure;
Taken from here.
Also, once you get that error removed, you can use SQL's nvl() function in your procedure to specify what you want displayed when a NULL value is encountered.
Sorry about not properly addressing the question...must have misunderstood you. Here's an example of nvl, which I think might address it a little better?
select NVL(supplier_city, 'n/a')
from suppliers;
The SQL statement above would return 'n/a' if the supplier_city field contained a null value. Otherwise, it would return the supplier_city value.

SQL Stored Procedure to return results with multiple parameters

I want to create a stored procedure that returns the results of a search for "similar records". The stored procedure i currently have is as follows
CREATE PRODECURE [dbo].[viewSimilarRecords]
#recordType varchar(25),
#recordSource varchar(25)
AS
SELECT * FROM myTableName
WHERE
(recordtype = #recordType) AND
(recordsource = #recordSource)
GO
The problem i'm having is that when it executes i get a return value 0 and the resulting rows do not show up (SQL Server Management Studio).
I can confirm i'm passing the correct values in the params and that multiple records do exist.
This is the execute Statement when it's ran
USE [databasename]
GO
DECLARE #return_value int
EXEC #return_value = [dbo].[ViewSimilarRecords]
#recordType = N'Error',
#recordSource = N'Local'
SELECT 'Return Value' = #return_value
GO
Without more information to go on, I'm just guessing but perhaps no data matches your parameters. the procedure is viewSimilarRecords and not viewExactmatchingRecords. The way it is coded you must have an exact match for every parameter. Possibly you need to try something like this:
CREATE PROCEDURE [dbo].[viewSimilarRecords]
#recordType varchar(25),
#recordSource varchar(25)
AS
SELECT * FROM myTableName
WHERE
(recordtype =#recordType OR #recordType IS NULL) AND --<<change to OR??
(recordsource=#recordSource OR #recordSource IS NULL)
--^^^^^^^^^^^^^^^^^^^^^^^^
GO
you could take a look at this: Dynamic Search Conditions in T-SQL by Erland Sommarskog
Check all of the little (obviously, it could never be that...) things...
1) Verify the procedure exists. In your create statement, you mis-spelled p-r-o-*c-e-*d-u-r-e
2) When you execute, you're enforcing unicode (N') on non-unicode variables.
#recordType = N'Error', #recordSource = N'Local'
Try this instead:
#recordType = 'Error', #recordSource = 'Local'
3) Instantiate the parameters and run just this part of the procedure directly (highlight and press F5) to make sure you get results.
ECLARE #recordType varchar(25)
ECLARE #recordSource varchar(25)
SET #recordType = 'Error'
SET #recordSource = 'Local'
SELECT * FROM myTableName
WHERE
(recordtype = #recordType) AND
(recordsource = #recordSource)
4) Run this directly, all by itself (if item 3 above was successful to verify that you're in the right database:
EXEC [dbo].[ViewSimilarRecords] 'Error','Local'

Query returns correct results, stored proc does not. Need a second set of eyes

My Query:
select
*
from
meet_cert_credit
where
conf_number = '1132'
and type_of_professional = 'Certified Hazardous Materials Managers'
My stored procedure:
ALTER PROCEDURE [dbo].[sp_check_credit_info]
#cn varchar = NULL,
#top varchar = NULL
AS
BEGIN
SET NOCOUNT ON;
select
*
from
meet_cert_credit
where
Conf_number = #cn
and type_of_professional = #top
END
Calling my stored proc:
exec sp_check_credit_info '1132', 'Certified Hazardous Materials Managers'
When running the query, it returns results. When running the stored procedure, I get nothing.
Am I insane?
You need to give a length to your varchar stored procedure parameters.
e.g. use #cn varchar(30) not #cn varchar
Currently everything you are passing in is getting truncated to 1 character so you are effectively doing the following search.
select
*
from
meet_cert_credit
where
Conf_number = '1'
and type_of_professional = 'C'
Hence no results.

Stored Procedure NULL Parameter

I have a database table I am trying to do an UPDATE/INSERT to with a stored procedure. Let's define the table like so:
CREATE TABLE Foo
(
Id INT IDENTITY(1, 1),
Name VARCHAR(256) NOT NULL,
ShortName VARCHAR(32),
Sort INT
);
I have written a stored procedure similar to the following:
CREATE PROCEDURE Put_Foo
(
#Id INT = NULL OUTPUT,
#Name VARCHAR(256),
#ShortName VARCHAR(32) = NULL,
#Sort INT = NULL
)
AS
BEGIN
SET NOCOUNT ON;
SELECT
#Id = F.Id
FROM
Foo AS F
WHERE
F.Name = #Name;
IF (#Id IS NOT NULL)
BEGIN
UPDATE
Foo
SET
ShortName = #ShortName,
Sort = #Sort
WHERE
Id = #Id;
END
ELSE
BEGIN
INSERT
INTO Foo
(
Name,
ShortName,
Sort
)
VALUES
(
#Name,
#ShortName
#Sort
);
SET #Id = SCOPE_IDENTITY();
END
RETURN;
END;
I've greatly simplified the data structures I am dealing with but I hope this serves my point. My question is in regards to how the parameters are processed. Is there a way to determine within the procedure if #Sort was passed in as NULL or set NULL by the default declaration in the parameter list?
EDIT:
The purpose of this is that I don't want NULL parameters to override any columns in the UPDATE statement unless they are explicitly passed in that way.
No, you can't detect how #Sort became NULL. If your goal is to capture when it is explicitly set versus it being set by the default, I would suggest using a different default value (maybe one that wouldn't normally be used, like -1). Then you can assume that if #Sort is NULL, it was explicitly passed in, but if it is -1, you know it was set by default.
I think this is what your looking for. If one of the parameters is null, it will updatedit with the value in the database. The other option is update one column at a time.
UPDATE Foo
SET
ShortName = ISNULL(#ShortName, ShortName)
, Sort = ISNULL(#Sort, Sort)
WHERE Id = #Id;
Take out the default value and and then the code calling the proc must provide a value (either a real value or NULL)