SCOPE_IDENTITY returning null values sometimes - sql

I've got a SQL query where sometimes the IDENTITY value from the INSERT [Form] in the first statement comes back as NULL and causes the rest of the statements in the batch to fail.
I included the second block of the procedure to indicate where it fails due to a conflict with the FK constraint on the table Form
Previously, it had been using SCOPE_IDENTITY, but I had changed it to use SQL Server's temp tables and an OUTPUT clause to see if it would alleviate the issue and it hasn't.
This procedure is being called from an ASP.NET webforms application, and the call is initiated by a Web API call running in that application.
Previously before this was ever made into a Web API call, it was initiated by a click event in the webforms application. Back then I would see this error occur every now and again.
With more and more use of the application and heavier loads, this seems to occur more frequently.
I checked and there are no triggers on the table that are firing off. I can't think of any way to replicate or track down the problem.
In most cases the procedure works fine, but every now and again it doesn't, and I'm unsure why. Sometimes I'll see a log of the error occurring multiple times in a row as the user attempts to save what they are working on. If they try enough times, it seems to work.
I've looked into using other forms of identity retrieval like ##IDENTITY and those won't work for what I need.
Is there something I'm missing?
ALTER PROCEDURE [dbo].[IC_Note_UpdateForm]
#FormID int = -1 OUTPUT, #ConsultFormID int = -1 OUTPUT, #PatientSignature bit, #DSPSignature bit, #Editable bit, #Narrative nvarchar(MAX) = NULL, #SignificantIssues nvarchar(MAX), #UserID int, #DSPID int, #FormTypeID int, #ServiceID int, #ApptID int OUTPUT, #LocationID int, #LoggedInUser int, #PortalId int,#ClientNotes nvarchar(MAX)
WITH EXEC AS CALLER
AS
SET NOCOUNT ON;
--This is needed for whatever reason in order to get the correct date submitted. Using the function call inline causes weird stuff to happen
DECLARE #DateSubmitted DATETIME = dbo.GetLocalTime(default)
DECLARE #count int
--See if a record exists for the Form
SELECT #count = COUNT(FormId) FROM Form WHERE (formID = #FormID OR (apptID = #ApptID AND apptID >= 1))
if #count > 0 BEGIN
UPDATE dbo.Form SET
FormTypeID = #FormTypeID,
patientSignature = #PatientSignature,
dspSignature = #DSPSignature,
editable = #Editable,
dateSubmitted = #DateSubmitted
WHERE
formID = #FormID
IF #Editable = 0 BEGIN
exec IC_NoteAudit_Insert #FormId, #DSPID, 'SUBMITTED'
END ELSE BEGIN
exec IC_NoteAudit_Insert #FormID, #DSPID, 'UPDATED'
END
END ELSE BEGIN
DECLARE #tempForm TABLE (FormId int)
INSERT dbo.Form (
PortalId
,userID
,dspID
,dateSubmitted
,patientSignature
,dspSignature
,editable
,approved
,dateApproved
,rejected
,formTypeID
,paid
,billed
,serviceID
,apptID
) OUTPUT inserted.formId INTO #tempForm
VALUES (
#PortalId
,#UserID -- userID - int
,#DSPID -- dspID - int
,#DateSubmitted -- dateSubmitted - datetime
,#PatientSignature -- patientSignature - bit
,#DSPSignature -- dspSignature - bit
,#Editable -- editable - bit
,null -- approved - bit
,null -- dateApproved - datetime
,null -- rejected - bit
,#FormTypeID -- formTypeID - int
,0 -- paid - bit
,0 -- billed - bit
,#ServiceID -- serviceID - int
,#ApptID -- apptID - int
)
--This was SET #FormId = SCOPE_IDENTITY() before and had the same NULL FK constraint occur
SET #FormID = (SELECT TOP 1 FormId FROM #tempForm)
END
--Move these out of scope of the IDENTITY retrieval
IF #count = 0 BEGIN
exec IC_NoteAudit_Insert #formID, #DSPID, 'CREATED'
IF #Editable = 0 BEGIN
exec IC_NoteAudit_Insert #formID, #DSPID, 'SUBMITTED'
END
END
SELECT #count = COUNT(FormId) FROM ConsultForm WHERE formId = #FormID
IF #count > 0 BEGIN
--See if a row exists for the ConsultForm
UPDATE dbo.ConsultForm SET
narrative = #Narrative,
significantIssues = #SignificantIssues
WHERE
consultFormID = #ConsultFormID
AND formID = #FormID
END ELSE BEGIN
DECLARE #tempConsultForm TABLE (ConsultFormId int)
INSERT dbo.ConsultForm (
PortalId
,formID
,dateOfService
,timeIn
,timeOut
,narrative
,significantIssues
,locationOfService
) OUTPUT inserted.ConsultFormID INTO #tempConsultForm
VALUES (
#PortalId,
#FormID -- formID - int
,null -- dateOfService - datetime
,null -- timeIn - datetime
,null -- timeOut - datetime
,#Narrative -- narrative - nvarchar(MAX)
,#SignificantIssues -- significantIssues - nvarchar(MAX)
,null -- locationOfService - nvarchar(MAX)
)
/*** Failure with FK constraint happens here, #FormId is NULL ***/
SET #ConsultFormID = (SELECT TOP 1 ConsultFormId FROM #tempConsultForm)
END ````

Which Version of SQL, seems like a bug in SQL. Can You use RAISERROR inside the SQL Code to make sure whether it is an issue in SQL itself.

Related

How to assign value in stored procedure variable when executing it?

This is my SQL code
CREATE PROC sp_procedure
(#Name VARCHAR(50),
#Stock numeric)
AS
DECLARE #Code CHAR(4)
BEGIN
UPDATE tbProcedure
SET Name = #Name, Stock = #Stock
WHERE Code = #Code
SELECT * FROM tbProcedure
END
Then I execute the code like this
EXEC sp_procedure 'Name',15,2
Then I got error result saying too many argument specified.
I also tried this
EXEC sp_procedure 'Name',15
It doesn't return an error, but 0 rows affected.
I want to assign #Code value when I execute the stored procedure, is it even possible to do that ?
EDIT:
Sorry, it's actually a CHAR(4), not INT
Again sorry, I just copy paste all code without looking at it first, above is the actual code, I am very sorry for the confuse...
It looks like you are providing the parameters in a different order than you have declared them. Choice is declared before Name but you are providing Name first when executing the procedure. Also, don't you want your update statement to say "code = #choice"? The local variable is undefined and not needed.
For Microsoft SQL Server
For your problem:
(don't use sp prefix) and (#code is not a parameter so we cannot pass value, it is local variable)
If you are having some problems with IF then, make #code as parameter but pass null value when needed and make that null check in procedure.
See below to assign values:
USE [databasename]
GO
DECLARE #return_value int
EXEC #return_value = <procedurename>
#stringvariable = N'<stringvalue>',
#Flag = <integervalue>
SELECT 'Return Value' = #return_value (return value if any)
GO
I have come to a conclusion that its impossible to combine branching stored procedure if one of the branch do not use all the parameters.
And because of that I have write 2 stored procedures, below is my full code :
For insert procedure: [this is the procedure that using a variable]
CREATE PROC InsProcedure
(#Name VARCHAR(50),
#Stock numeric)
AS
DECLARE #CODE INT
DECLARE #CODE2 CHAR(4)
BEGIN
// Creating custom auto number
SET #CODE = 0
SELECT #CODE = ISNULL(MAX(CAST(KodeBarang AS INT)), 0)
FROM tbProcedure
SET #CODE2 = #CODE + 1
SET #CODE2 = REPLICATE('0', 4 - LEN(#CODE2)) + #CODE2
// End Custom auto number
INSERT INTO tbProcedure VALUES(#CODE2, #Name, #Stock)
SELECT * FROM tbProcedure
END
For update, delete, and show procedure:
CREATE PROC OtherProcedure
(#choice int, // this is for branching option later
#Code CHAR(4),
#Name VARCHAR(50),
#Stock numeric)
AS
IF #choice = 1
BEGIN
UPDATE tbProcedure
SET Name = #Name, Stock = #Stock
WHERE Code = #Code
SELECT * FROM tbProcedure
END
ELSE IF #choice = 2
DELETE FROM tbProcedure
WHERE KodeBarang = #Code
ELSE
SELECT * FROM tbProcedure
and use the procedures like this :
InsProcedure 'Laptop', 5
Result:
00001, Laptop, 5
Branch 1 [Update]
OtherProcedure 1, 'Laptops', 10, 00001
Result:
Before : 00001, Laptop, 5
After : 00001, Laptops, 10
Branch 2 [Delete]
OtherProcedure 2, '', 0, 00001 // Delete record where Code is '00001'
Branch 3 [Show]
OtherProcedure 3, '', 0, 0 // You can also use any number besides 3
First of all, your variable #Stock doesn't return decimal because it's INT variable, you might try float or something else. It's the reason why it shows an error if you give it "15,2"
WHERE Code = #Code
And 0 rows affected was caused because you didn't set that variable properly.
You have to assign a valid value to it.

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?

"Subsequent parameters" error in SSRS?

I'm getting a strange error in SSRS, in a report (which gets put into a sproc) with many drop-down parameters:
Query execution failed for dataset 'DataSet1'.
Must pass parameter number 3 and subsequent parameters as '#name = value'. After the form '#name = value' has been used, all subsequent parameters must be passed in the form '#name = value'
I'm lost on what's going on here - what is meant by #name = value .
I searched online, someone mentioned that you should alter the stored-procedure?
Here is how the top half of my stored-proc looks:
USE [FederatedSample]
GO
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
ALTER PROCEDURE [dbo].[prc_RPT_Select_BI_Completes_Data_View_2]
#FromDate DATETIME,
#ToDate DATETIME,
#AccountIDs VARCHAR(max) = null,
#ClientIDs VARCHAR(max) = null,
#SupplierIDs VARCHAR(max) = null,
#CompleteType INT = NULL,
/*
* 0 - Routed
* 1 - Targeted
* 2 - Offerwall
*/
#SourceType BIT = NULL,
/*
* Works if #AccountID is not null
* (should only be used if #AccountID has a single value)
*
* 0 - Owned by #AccountID
* 1 - External (not owned by #AccountID)
*/
#SurveyStatus INT = NULL,
/*
* NULL - All Surveys
* 0 - Completes Approved Surveys
* 1 - Invoiced Surveys
*/
#IsSupplierUser BIT = 0
/*
* used to decide whether to display FEDSurveyName or SupplierSurveyName
*/
AS
BEGIN
SET NOCOUNT ON
DECLARE #SQL NVARCHAR(MAX) = N'',
#Params NVARCHAR(MAX)
IF #AccountIDs is not null
BEGIN
SET #SQL += N'DECLARE #AccountIDs VARCHAR(MAX) = #pAccountIDs; '
END
IF #ClientIDs is not null
BEGIN
SET #SQL += N'DECLARE #ClientIDs VARCHAR(MAX) = #pClientIDs; '
END
IF #SupplierIDs is not null
BEGIN
SET #SQL += N'DECLARE #SupplierIDs VARCHAR(MAX) = #pSupplierIDs; '
END
SET #SQL += N'
SELECT bi.SupplierID as ''Supplier ID''
,bi.SupplierName as ''Supplier Name''
,bi.PID as ''PID''
,bi.RespondentID as ''Respondent ID''
,lk_slt.Name as ''Entry Link Type''
,ts.SurveyNumber as ''Initial Survey ID'''
And later in the stored proc. it does stuff like this to split strings:
IF #AccountIDs is not null
BEGIN
SET #SQL += CHAR(13) + CHAR(9)
SET #SQL += N' and bi.AccountID in (SELECT CAST(val as INT) FROM dbo.Split(#AccountIDs, '','
When invoking a stored procedure, you either can pass the parameters by position (not a good idea) or by Name (a better approach IMHO).
EXEC dbo.MyStoredProcedure '12/31/2012', 1; -- Not a great way to pass parameters
EXEC dbo.MyStoredProcedure #AsOfDate = '12/31/2012', #AccountID = 1; -- A better way
From the error message you are receiving, I suspect that SSRS is using the second approach and is running into an issue with the third parameter being provided to the stored procedure.
Without more information to go off of it is difficult to provide you with an exact explanation for the error (the stored procedure would perhaps help), an educated guess is that the way the parameters are being provided for Account IDs, Client IDs and Supplier IDs isn't quite correct. Specifically, I think the problem might be that you are providing multiple identifiers delimited by a comma.
You might try passing a single Account ID, Client ID and Supplier ID to see if you still receive the error. I would also try to look at the stored procedure (or talk to the DBA \ Developer who wrote it) to ascertain the intended usage of the stored procedure.
I got a similar message when passing a comma instead of a full stop for a decimal value in one of the parameters of a stored procedure.
Here is a simplified example of what happened.
The following command string was erroneously generated with the value of #param2 with a comma instead of a full stop, causing a misunderstanding of the number of parameters.
EXEC myStoredProc #param1 = 1, #param2 = 0,5 ,#param3 = 'something'

Does ms sql server not take ReadPast Hint in Functions?

I have a pending order table with a check constraint to prevent people from ordering an item we don't have in stock. This required me to create a counter function to decide if an insert can happen or not. It works until there is 1 item left in inventory then I get a message that we are out of stock of the item. I thought it was a dirty read issue but even after interducing a ReadPast hint I still see this behavior. Is there some other factor causing this problem? Or do I need to setup the isolation level differently?
I have tried calling this function with the sprokID and it returns true which is why I am thinking during insert there is a dirty read taking place.
ALTER TABLE [dbo].[PendingSprokOrders] WITH CHECK ADD CONSTRAINT [CK_SprokInStock] CHECK (([dbo].[SprokInStockCount]([SprokID])=(1)))
FUNCTION [dbo].[SprokInStockCount] ( #SprokId INT )
RETURNS INT
AS
BEGIN
DECLARE #Active INT
SET #Active = ( SELECT COUNT(*)
FROM [PendingSprokOrders] AS uac WITH(READPAST)
WHERE uac.SprokID = #SprokId
)
DECLARE #Total INT
SET #Total = ( SELECT
ISNULL(InStock, 0)
FROM SprokInvetory
WHERE id = #SprokId
)
DECLARE #Result INT
IF #Total - #Active > 0
SET #Result = 1
ELSE
SET #Result = 0
RETURN #Result;
END;
The math is off. Instead of:
IF #Total - #Active > 0
SET #Result = 1
ELSE
SET #Result = 0
it should be:
IF #Total - #Active > -1
SET #Result = 1
ELSE
SET #Result = 0
That's because your constraint function can see the row that you are attempting to add and is counting it.
Yes it does but your set#total statements are contradictory also there are a couple breaks in your code.

Getting primary key of each row inside for loop in sql server 2005

I want to write a trigger in SQL Server 2005 in which inside the for loop I want to update every row of a particular table based on its primary key. The problem is how should I get primary key of each row inside for loop?
Please Help
sorry for not mentioning above details
Table UserPersonalInfo
UserId varchar(50) Primary Key
FirstName varchar(50)
MiddleName varchar(50)
LastName varchar(50)
UserName varchar(50)
Password varchar(50)
ContactNo bigint
Verified bit
Address varchar(100)
EmailId varchar(100)
RoleId int
CurrentFine money
Photo image
Table CurrentlyIssuedBook
Userid varchar(50) Primary Key
BookId varchar(50)
IssuedDate datetime
ExpectedReturnDate datetime
ISBN varchar(50)
Table CurrentDate
date datetime
Above are the two tables
Now what I am trying to do is...
Everytime I run my C# application I will try to update date in CurrentDate table with actual current date. If the update is successful then the trigger will run.
Inside trigger I want to update fine for each user in the UserPersonalInfo table. For that I have thought of using a loop but how will get primary key value of each row from UserInfo table?
My Fine Calculation logic is a follows
totalfine = 0
x = currentdate - ExpectedReturnDate
y = x/30
z = x%30
for(int i=0; i <y; i++)
{
totalfine = totalfine + (2^i * 4 * 30);
}
totalfine = totalfine + (2^i * 4 * z);
Now please suggest me what should I do?
Don't use a loop.
You can update every row in the table just by using an update statement with no where clause, eg:
declare #currentdate datetime
select #currentdate = date from currentdate
update userinfo
set fine = case when #currentdate < getdate() then 100 else 0 end
Of course, I don't know what your fine calculation is, so the above is just a trivial example.
It's possible to put complex calculations involving other tables and so on into an update statement as above, and it will be much faster than a loop. Having said that, if you really want to use a loop, you need to use a cursor. Maybe something like:
declare #ID int
declare cur_loop cursor fast_forward for
select UserId from UserInfo
open cur_loop
fetch next from cur_loop into #ID
while ##FETCH_STATUS=0 begin
-- your code here
update UserInfo set fine = (calculation result) where UserID=#ID
fetch next from cur_loop into #ID
end
close cur_loop
deallocate cur_loop
Cant you do something like this:
CREATE TRIGGER tr_CurrentDateUpdate
ON CurrentDate
AFTER UPDATE
AS
BEGIN
-- SET NOCOUNT ON added to prevent extra result sets from
-- interfering with SELECT statements.
SET NOCOUNT ON;
UPDATE UserInfo
SET fine = 6
WHERE userid like '%foo%'
UPDATE UserInfo
SET fine = 7
WHERE userid like '%baa%'
END
GO
Where the "where" clauses are your criteria for selecting which users you want to change and the fine = your calculations?
Also, I didnt test this but, if you have your calculation in a UDF I think you could just put something like this where i had my update statements:
DECLARE #InsertedDate Datetime
SELECT #InsertedDate = date
FROM inserted
UPDATE u
SET u.fine = cf.FineResult
FROM UserInfo u
cross apply dbo.CalculateFine(u.UserId, #InsertedDate) cf
Where CalculateFine UDF contains the logic on how to convert the user details into their fine.
With a UDF something like this:
CREATE FUNCTION CalculateFine (#UserId varchar(50), #CurrentDate datetime)
RETURNS money
AS
BEGIN
--TODO get expected return date ExpectedReturnDate from CurrentlyIssuedBook for that #UserId
--TODO Do you need all these delcarations?
DECLARE #x AS INT
DECLARE #fine AS MONEY
DECLARE #y AS INT
DECLARE #z AS INT
SET #x = DATEDIFF(dd, #CurrentDate, ExpectedReturnDate)
SET #y = #x/30
SET #z = #x%30
-- TODO convert your logic here....
--Return your answer
RETURN #fine
END