DECLARE #id bigint=0,
#id int=0,
#name varchar(50) = '36',
#marks int = 'SDFGS'
#Op varchar(50) = 'UPSERT'
IF(#Op='UPSERT')
BEGIN
INSERT INTO tbl_student
(name, marks)
VALUES
(#name, #marks)
SELECT SCOPE_IDENTITY()
END
ELSE
BEGIN
UPDATE tbl_student SET
name = #name,
marks = #marks
WHERE id = #id
SELECT 'Success'
END
It throw error 'Conversion failed when converting the varchar value 'SDFGS' to data type int.'
I want to handle this error.
If error then it will be return 'Error' string.
You can handle this error using TRY... CATCH Block
Begin
declare #msg varchar(100)
Begin try
DECLARE #id bigint=0,#name varchar(50) = '36',#marks int = 'SDFGS',#Op varchar(50) = 'UPSERT'
IF(#Op='UPSERT')
BEGIN
INSERT INTO tbl_student
(name, marks)
VALUES
(#name, #marks)
SELECT SCOPE_IDENTITY()
END
ELSE
BEGIN
UPDATE tbl_student SET
name = #name,
marks = #marks
WHERE id = #id
SELECT 'Success'
Set #msg='Success'
END
End try
Begin catch
SELECT 'Error'
Set #msg='Error'
End catch
End
You can use TRY ... CATCH
https://msdn.microsoft.com/en-us/library/ms175976.aspx - there is a sample code here.
The error says it all, you are trying to put a string value in an int datatype and hence the error. If you want to catch this error then try to use TRY...CATCH. Something like
BEGIN TRY
-- Your code.
END TRY
BEGIN CATCH
-- Catch the exception/error here.
END CATCH;
Related
I'm getting an error message in a stored procedure, saying that I can't insert a NULL value into a table, when I should be getting errors earlier in the code if the value is null.
Here's the relevant part of the stored procedure:
CREATE PROCEDURE [dbo].[udp_AddUpdateStaffVariable]
-- Add the parameters for the stored procedure here
#StaffID int=null,
#VariableTypeID int,
#VariableIntValue int=null,
#VariableVarcharValue varchar(max)=null,
#VariableDatetimeValue datetime=null,
#VariableDecimalValue decimal=null
AS
BEGIN
-- SET NOCOUNT ON added to prevent extra result sets from
-- interfering with SELECT statements.
SET NOCOUNT ON;
-- Insert statements for procedure here
BEGIN TRY
DECLARE #PrintOutput varchar(150)
SET #PrintOutput = '#StaffID = ' + CASE WHEN #StaffID = NULL THEN 'Null' ELSE CONVERT(varchar(20), #StaffID) END
RAISERROR (#PrintOutput, 10, 1) WITH NOWAIT
IF (#StaffID = NULL) -- If the staffid of the current user was not supplied, find it in the Staff table
BEGIN
DECLARE #CurrentUser nvarchar(255) = SUSER_SNAME();
SELECT #StaffID = [StaffID] FROM [dbo].[Staff] WHERE [UserName] = #CurrentUser;
SET #PrintOutput = '#StaffID = ' + CASE WHEN #StaffID = NULL THEN 'Null' ELSE CONVERT(varchar(20), #StaffID) END
RAISERROR (#PrintOutput, 10, 1) WITH NOWAIT
IF #StaffID = NULL -- raise error if staffid wasn't found
BEGIN
RAISERROR (50001 --error number
, 16 -- severity
, 1 --state
, #CurrentUser -- parameter
)
END
END
-- Get the variable data type (used to determine where the variable is stored)
DECLARE #VarDataTypeDesc varchar(20)
DECLARE #StaffVarID int
SELECT #VarDataTypeDesc = dt.[StaffVariableDataType]
FROM [list].[DataTypes] dt INNER JOIN [list].[StaffVariableTypes] svt ON dt.DataTypeID = svt.DataTypeID
WHERE svt.VariableTypeID = #VariableTypeID
-- update or add the staff variable
IF EXISTS (SELECT 1 FROM [dbo].[StaffVariables] WHERE StaffID = #StaffID AND [VariableTypeID] = #VariableTypeID) -- update
BEGIN
IF #VarDataTypeDesc = 'int'
BEGIN -- only update here - other data types are updated further down
UPDATE [dbo].[StaffVariables] SET VariableIntValue = #VariableIntValue WHERE StaffID = #StaffID AND VariableTypeID = #VariableTypeID
END
ELSE -- StaffVariableID is only needed if the variable type is not int
BEGIN
SELECT #StaffVarID = StaffVariableID FROM [dbo].[StaffVariables] WHERE StaffID = #StaffID AND [VariableTypeID] = #VariableTypeID
END
END
ELSE -- insert
BEGIN
IF #VarDataTypeDesc = 'int'
BEGIN
INSERT INTO [dbo].[StaffVariables] (StaffID, VariableTypeID, VariableIntValue)
VALUES (#StaffID, #VariableTypeID, #VariableIntValue)
END
ELSE -- StaffVariableID is only needed if the variable type is not int
BEGIN
DECLARE #StaffVarIDTbl table(ID int)
INSERT INTO [dbo].[StaffVariables] (StaffID, VariableTypeID, VariableIntValue)
OUTPUT INSERTED.[StaffVariableID] INTO #StaffVarIDTbl
VALUES (#StaffID, #VariableTypeID, #VariableIntValue)
SELECT #StaffVarID = ID FROM #StaffVarIDTbl
END
END
-- Cutting out the section where I deal with other variable types besides int here - not relevant to this problem
END TRY
BEGIN CATCH
DECLARE #ErrorMessage NVARCHAR(4000);
DECLARE #ErrorSeverity INT;
DECLARE #ErrorState INT;
SELECT
#ErrorMessage = ERROR_MESSAGE(),
#ErrorSeverity = ERROR_SEVERITY(),
#ErrorState = ERROR_STATE();
-- Use RAISERROR inside the CATCH block to return error
-- information about the original error that caused
-- execution to jump to the CATCH block.
RAISERROR (#ErrorMessage, -- Message text.
#ErrorSeverity, -- Severity.
#ErrorState -- State.
);
END CATCH;
END
Here's the test procedure run code:
DECLARE #return_value int
EXEC #return_value = [dbo].[udp_AddUpdateStaffVariable]
#VariableTypeID = 1,
#VariableIntValue = 10
SELECT 'Return Value' = #return_value
GO
...and here's the response:
Msg 50000, Level 16, State 2, Procedure dbo.udp_AddUpdateStaffVariable, Line 130 [Batch Start Line 2]
Cannot insert the value NULL into column 'StaffID', table 'SnippingDbName.dbo.StaffVariables'; column does not allow nulls. INSERT fails.
(1 row affected)
Completion time: 2020-06-01T21:17:08.2049072-05:00
So... here's the question. The error seems to indicate that it either never ran the whole, if #StaffID = NULL portion of the code, or it did, and didn't find the StaffID and set the #StaffID variable. But if that were the case, why can't I see the results of my earlier RAISERROR statements?
I initially tried PRINT and switched to RAISERROR when PRINT wasn't working.
SQL Server 2017 Developer Edition, SSMS 15.0.18183.0
It was a syntax error, that people commenting on the question figured out. IF (#StaffID = NULL) should have been, IF (#StaffID IS NULL) Fixing that in all places in the procedure fixed the problem, and altering my test Staff record so UserName doesn't match SUSER_SNAME() resulted in the expected error.
I have this store procedure and I lets assume the user want to update some fields not all of it, what should I add on the update section
CREATE PROCEDURE [dbo].[helpChainAllCRUD]
#action char(1),
#lineId char(2),
#lineShift smallint = NULL,
#sequence smallint = NULL,
#Role VARCHAR(32) = NULL,
#radioChannel VARCHAR(16)= NULL,
#officePhone VARCHAR(16)= NULL,
#cellPhone VARCHAR(16)= NULL
as
IF(#action = 'I')
BEGIN TRY
BEGIN TRAN
INSERT INTO [dbo].[tbl_helpChain] (lineId,lineShift,sequence,Role,radioChannel,officePhone,cellPhone)
VALUES (#lineId ,#lineShift,#sequence,#Role,#radioChannel,#officePhone,#cellPhone)
COMMIT
END TRY
BEGIN CATCH
IF ##TRANCOUNT >0
ROLLBACK
END CATCH
IF(#action = 'U')
BEGIN TRY
BEGIN TRAN
UPDATE [dbo].[tbl_helpChain] SET lineShift=#lineShift,sequence=#sequence,Role=#Role,radioChannel=#radioChannel,officePhone=#officePhone,cellPhone=#cellPhone WHERE lineId=#lineId
COMMIT
END TRY
BEGIN CATCH
IF ##TRANCOUNT >0
ROLLBACK
END CATCH
IF(#action = 'D')
BEGIN TRY
BEGIN TRAN
Delete from [dbo].[tbl_helpChain] WHERE lineId=#lineId
COMMIT
END TRY
BEGIN CATCH
IF ##TRANCOUNT >0
ROLLBACK
END CATCH
IF(#action = 'S')
Begin
select lineId,lineShift,sequence,Role,radioChannel,officePhone,cellPhone from [dbo].[tbl_helpChain]
end
GO
I'm sure there are much better solutions, but a quick and easy solution might be to use very ugly dynamic SQL
DECLARE #QueryText nvarchar(max) = 'UPDATE [dbo].[tbl_helpChain] SET '
IF #radioChannel<> NULL
#QueryText = #QueryText + 'RadioChannel=#radioChannel'
EXECUTE SP_EXECUTESQL #QueryText
If i.e. null means don't update you could simply write
SET lineShift = COALESCE(#lineShift,lineShift), ...
or you take another special value and a case-expression
SET lineShift = CASE WHEN #lineShift = -1 then lineShift else #lineShift end,
or you give extra boolean parameters for each column for use in the case-expression
UPDATE helpChain
SET
fullName = ISNULL(#fullName,fullName),
lineShift = ISNULL(#lineShift,lineShift),
sequence = ISNULL(#sequence,sequence),
Role = ISNULL(#Role,Role),
radioChannel = ISNULL(#radioChannel,radioChannel),
officePhone = ISNULL(#officePhone,officePhone),
cellPhone = ISNULL(#cellPhone,cellPhone)
WHERE lineId = #lineId
I coded this:
BEGIN TRY
SELECT #TestUserId = UserId,
#TestCreatedBy = CreatedBy
FROM UserTest
WHERE UserTestId = #UserTestId
IF ( #TestUserId != #UserId & #TestCreatedBy != #UserID)
BEGIN
THROW 50000,'THROW TEST',1
END
END TRY
BEGIN CATCH
SET #RC = 1
SET #ErrorMessage = ERROR_MESSAGE()
RETURN
END CATCH
I am having a problem with the line starting with IF. It gives an error saying:
! - Incorrect syntax
Also the THROW line shows an error
Can someone advise me how I can make this throw and error correctly and how I can catch it. Can I catch in a block specific to the 50000 code?
I hope #UserId is defined somewhere. The problem is with & - replace it with AND.
Try this
BEGIN TRY
SELECT #TestUserId = UserId,
#TestCreatedBy = CreatedBy
FROM UserTest
WHERE UserTestId = #UserTestId
IF ( #TestUserId != #UserId AND #TestCreatedBy != #UserID)
BEGIN
raiserror('THROW TEST',16,1)
END
END TRY
BEGIN CATCH
SET #RC = 1
SET #ErrorMessage = ERROR_MESSAGE()
RETURN
END CATCH
I have this stored procedure:
CREATE PROCEDURE sp_purchase_test
#AdminTestId int,
#PurchaseDate DATETIME,
#UserId int
AS
INSERT INTO dbo.UserTest
(
AdminTestId,
PurchaseDate,
UserId,
Sequence
)
SELECT AdminTestId,
#PurchaseDate,
#UserId,
1
FROM AdminTest
WHERE AdminTestId = #AdminTestId
RETURN 0
I have a Unique index on UserTest.AdminTestId and sequence so there is a possibility that this stored
procedure will not work. How can I make the stored procedure return a non zero if the insert failed?
From what I understand it might be a good idea to use a TRY-CATCH. However if I do this should I do it like this:
BEGIN CATCH
SELECT -1
END CATCH
or
BEGIN CATCH
RETURN 99
END CATCH
I am confused between a SELECT at the end of a stored procedure and use or RETURN
Here's how I call the stored procedure. Ideally I would like to be able to return an error message from my C# method.
var sql = #"dbo.sp_purchase_test #AdminTestId,
#PurchaseDate,
#UserId";
SqlParameter[] parameters = new SqlParameter[]
{
new SqlParameter("#AdminTestId", adminTestId),
new SqlParameter("#PurchaseDate", DateTime.UtcNow),
new SqlParameter("#UserId", Int32.Parse(User.Identity.GetUserId()))
};
var result = db.Database.ExecuteSqlCommand(sql, parameters);
await db.SaveChangesAsync();
return Ok();
you can use output parameter
CREATE PROCEDURE test
#Value1 nvarchar(1),
#Value2 nvarchar(1),
#IsInserted bit output
AS
BEGIN
BEGIN TRY
INSERT INTO [dbo].[table_name]
([col_1]
,[col_2])
VALUES
(#Value1,#Value2)
SET #IsInserted=1
END TRY
BEGIN CATCH
SET #IsInserted=0
END CATCH
RETURN #IsInserted
END
Execute Procedure:
DECLARE #IsInserted bit
EXEC [dbo].[test]
#Value1 = N'a',
#Value2 = N'b',
#IsInserted = #IsInserted OUTPUT
SELECT #IsInserted as N'IsInserted'
I have a stored procedure in production that does 2 things. It updates one table and then inserts a record into another. The first step (the update) seems to occur but we've found instances by examining the data where the second step did not occur. I have looked at the data and confirmed that it is not a data issue. I've confirmed that the queries return the appropriate data in order to ensure that the queries complete and in normal circumstances both should execute. I don't know if perhaps there is some sort of performance issue ... or blocking issue that is occurring on the second step that prevents that step from occurring.
The error handling for the stored procedure is as follows.
BEGIN TRY
BEGIN TRANSACTION;
-- perform update to data
-- insert record into second table.
IF ( ##ERROR = 0 AND ##TRANCOUNT > 0 )
COMMIT TRANSACTION;
END TRY
BEGIN CATCH
IF ( ##TRANCOUNT > 0 )
BEGIN
ROLLBACK TRANSACTION;
END
DECLARE #WebSafeErrorId INT;
EXEC dbo.spErrorInsert #WebSafeErrorId OUTPUT, 'Proc';
-- Reraise the error back to the client.
IF ( #WebSafeErrorId != 0 )
BEGIN
DECLARE #Error VARCHAR(20);
SET #Error = CAST( #WebSafeErrorId AS VARCHAR(20) );
RAISERROR( #Error, 11, 1 );
END
ELSE
BEGIN
RAISERROR( 'An error has occurred but there is no error to log.', 11, 1 );
END
END CATCH;
Surely if an error occurred in this procedure that cause the insert to not occur it would be logged and then raised. The code for spErrorInsert is below ...
CREATE PROCEDURE [dbo].[spErrorInsert]
#ReturnErrorId INT OUTPUT
, #ErrorSourceType VARCHAR(4) = NULL
, #ParentErrorId INT = NULL
, #StackTrace VARCHAR(MAX) = NULL
AS
SET NOCOUNT ON;
--SET XACT_ABORT ON;
-- Will indicate an error was not logged.
SET #ReturnErrorID = 0;
DECLARE
#ErrorSource VARCHAR(200)
, #ErrorMessage VARCHAR(MAX)
, #ComposedErrorMessage VARCHAR(MAX)
, #ErrorLine INT
, #ErrorSeverity INT
, #ErrorState INT
, #ErrorNumber INT;
SET #ErrorSource = ERROR_PROCEDURE();
SET #ErrorMessage = ERROR_MESSAGE();
SET #ErrorLine = ERROR_LINE();
SET #ErrorSeverity = ERROR_SEVERITY();
SET #ErrorState = ERROR_STATE();
SET #ErrorNumber = ERROR_NUMBER();
SET #ComposedErrorMessage = 'Message: Error occurred in procedure ' + CAST( #ErrorSource AS VARCHAR(MAX) )
+ ' on line ' + CAST( #ErrorLine AS VARCHAR(MAX) )
+ '. Error: ' + #ErrorMessage;
BEGIN TRY
INSERT INTO Errors(
ParentId
, ErrorSourceType
, ErrorSource
, [Message]
, [LineNo]
, Severity
, Stacktrace
, ts)
VALUES (#ParentErrorId
, #ErrorSourceType --#ErrorSourceType --- NOTE: move this into a parameter ...
, #ErrorSource
, #ComposedErrorMessage
, #ErrorLine
, #ErrorState
, #Stacktrace
, GETDATE()
);
SET #ReturnErrorId = SCOPE_IDENTITY();
END TRY
BEGIN CATCH
RAISERROR( 'An error has occurred but there is no error to log.', 11, 1 );
END CATCH;
I don't know if maybe there is a way to get a snapshot of what's going on the database at a specific time when a certain procedure is called ... I'm not sure how to determine if something isn't happening when it should? Are there any tools that I can make use of or sql features that I don't know about???
If you want to monitor the database, SQL Profiler is a good place to start but it is going to be deprecated.
Extended events are much more capable and I would suggest reading about those if you are really interested in monitoring what's going on.
As a thought, if your procedure code is using the same data to update the row as it is to insert to the other table, consider using OUTPUT.
Update table set col1 = 'value'
OUTPUT inserted.col INTO othertable
Where col3 = stuff
OUTPUT and OUTPUT INTO
Or if this is for some sort of Audit or Log table, you can use DELETED.col1
That will be the original value prior to it being updated. Note that INSERTED will return the value that you are updating or inserting, it's just called INSERTED for both.
If you have a copy of Visual Studio, try it. It allows you to step through stored procedures.
The approach I would try is to firstly take a copy of this procedure and comment out the try/catch. I have found that tsql does not raise errors if the error generating code is within a try/catch block - I guess this is the sql equivalent of an exception being handled by the catch clause.
I use a table in my database to keep a permanent record of errors as they occur (a trick I learned doing embedded programming)
The errors table creation code is :
CREATE TABLE dbo.errors (
id smallint NOT NULL IDENTITY(1, 1) PRIMARY KEY,
errordesc nvarchar (max) NOT NULL,
dateandtime smalldatetime NOT NULL, -- Date and time of last occurance
errorcount int NOT NULL) ;
My stored procedure for adding a record into the error table is:
CREATE PROCEDURE jc_diagnostics.jpwsp0005_adderrordescription(
#Errordescription nvarchar( max ))
AS
BEGIN
DECLARE
#Id smallint = 0 ,
#Currentcount int = 0;
IF((#Errordescription IS NULL) OR ( #Errordescription = ''))
BEGIN
SET #Errordescription = 'Error description missing';
END;
SELECT #Id = ( SELECT TOP ( 1 ) id
FROM jc_diagnostics.errors
WHERE errordesc = #Errordescription );
IF(#Id IS NOT NULL)
BEGIN
SELECT #Currentcount = (SELECT errorcount
FROM jc_diagnostics.errors
WHERE id = #Id );
SET #Currentcount = #Currentcount + 1;
UPDATE jc_diagnostics.errors
SET errorcount = #Currentcount
WHERE id = #Id;
UPDATE jc_diagnostics.errors
SET dateandtime = CONVERT(smalldatetime , GETDATE())
WHERE id = #Id;
END;
ELSE
BEGIN
--new entry
INSERT INTO jc_diagnostics.errors( errordesc ,
dateandtime ,
errorcount )
VALUES( #Errordescription ,
CONVERT(smalldatetime , GETDATE()) ,
1 );
END;
IF(#Id IS NULL)
BEGIN
SET #Id = SCOPE_IDENTITY( );
END;
RETURN #Id;
END;
The calling code when an error occurs is:
Declare #Failuredesc nvarchar(max) = 'Description of error';
EXEC #Retval = jc_diagnostics.jpwsp0005_adderrordescription #Failuredesc;
The return value #Retval contains the id of the record in the error table so you can look it up
Finally I would create some code to continuously call your procedure until an error is declared. You can then inspect the error table and see if this throws light on your problem.
Hope this helps.
Jude
Logically thinking - because you declare transaction before these 2 steps, any error would result in rollback of both transactions. So most likely there is no error at all here. I would suggest inspect your queries again as it seems that the problem could be in them rather than anywhere else. Please post the entire code if you like more suggestions.
Regards
Roman