I have started a SQL Server locally on my computer. Now I am trying to find a way to modify error messages I get in SSMS.
Well you can do a TRY-CATCH. Something like the following:
SET NOCOUNT ON
CREATE TABLE #tmp(TestCol varchar(20))
INSERT INTO #tmp VALUES('test')
BEGIN TRY
INSERT INTO #tmp VALUES('testtesttesttesttesttest')
END TRY
BEGIN CATCH
DECLARE #nErrorNum INT;
SELECT #nErrorNum = ERROR_NUMBER()
IF #nErrorNum = 8152
BEGIN
RAISERROR('String is too long',10,1);
END
ELSE
THROW
END CATCH
Related
In a procedure, I want to make a test then Raiserror when it's actually the case. But before that, I want to log the error in a table. My code is like this
CREATE PROCEDURE proc
#val VARCHAR(50)
AS
BEGIN
SET NOCOUNT ON;
SET XACT_ABORT OFF;
DECLARE #test VARCHAR(50)
SELECT #test = test
FROM test_table
WHERE ...
IF #test IS NULL
BEGIN
INSERT INTO log_table VALUES (#val);
RAISERROR ('Invalid value : %i', 16, 1, #val);
END
END
The code compiles. When executed with a bad value, the error is raised, but the insert is cancelled.
I tried turning xact_abort and nocount on and off but had no luck.
I tried encapsulating the insert request in BEGIN TRANSACTION/COMMIT but still get the same result.
What I noticed, my log_table which has an auto-increment id, gets incremented even when those inserts are being cancelled.
How can I raise and error but still persist the insert request?
Thanks
Consider using THROW instead:
CREATE TABLE dbo.log_table (val varchar(50));
GO
CREATE PROCEDURE dbo.[proc] #val varchar(50)
AS
BEGIN
SET NOCOUNT ON;
SET XACT_ABORT OFF;
DECLARE #test varchar(50); --As i never set this, it'll go into the IF
IF (#test IS NULL)
BEGIN
INSERT INTO log_table
VALUES (#val);
THROW 51000, N'Invalid value.', 1;
END;
END;
GO
EXEC dbo.[proc] #val = 'Some Value';
GO
SELECT *
FROM dbo.log_table;
GO
DROP PROC dbo.[proc];
DROP TABLE dbo.log_table;
DB<>Fiddle
In order to write to a log table you have to rollback any pending transaction. Otherwise your log table INSERT may be rolled back by the calling code, or may fail because the transaction is doomed.
So something like:
CREATE Procedure myproc
#val varchar(50)
as
begin
set nocount on
set xact_abort on
begin transaction;
begin try
-- do stuff
commit transaction;
end try
begin catch
if ##trancount > 0 rollback;
declare #error_message varchar(max) = error_message()
INSERT INTO log_table values (#val);
throw;
end catch
end
So apparently, my procedure was working as expected in SQLServer side. The problem was that I was calling this procedure from Java/Spring native query method and had to be annotated with #Modifying and #Transactional since it's doing insertions. Thus when an exception is caught, it was automatically rolled back.
I didn't find a quick solution to bypass Spring's transaction. Now I think all I have to do is, catch the exception in App layer and log to the log_table in app layer too
I'd like to request the servicename of an SQL Server. Usually I do this with
SELECT ##SERVICENAME
But this don't work's on Azure hosted Databases. I'm looking for a way to determine the servicename, with a try catch fallback if I'm connected to a azure database. The usual try catch works not for me - due to compile error for the statement. How can i catch this compile error ?
This statement works in both environments without compile error:
DECLARE #T TABLE(ServiceName NVARCHAR(255));
DECLARE #Rows INT
IF SERVERPROPERTY('Edition') != 'SQL Azure'
BEGIN TRY
INSERT #T(ServiceName) EXEC ('SELECT ##SERVICENAME')
END TRY
BEGIN CATCH
END CATCH
SELECT #Rows = COUNT(*) FROM #T
IF #Rows = 0
INSERT #T(ServiceName) Values ('SQL Azure')
SELECT * FROM #T
On Local Server:
On Azure:
USE ##version global variable instead.
DECLARE #version VARCHAR(200)
SELECT #version = SUBSTRING(##version, 15,5)
PRINT #version
Hope this helps.
I have to develop a WinCC Visual Basic Script management application. In this application I read an XML archive, and after that I put the information in a SQL database through a SQL INSERT query.
My problem is that I don't know how to do the error handling to view the SQL errors in VBScript MsgBox for example.
Activating the error handling with On Error Resume Next and after the evaluation of these errors with If Err.Number <> 0 Then ... the errors produced in SQL Server don't appear in VBScript.
If you want to get SQL Server error, You can use stored procedure with transaction to insert data into table:
create procedure dbo.InsertTable (
#param1 nvarchar(80)
,#param2 nvarchar(80)
,#error_text nvarchar(400) output)
as
begin
begin tran
begin try
insert into YourTable (column1, column2)
values (#param1, #param2)
end try
begin catch
set #error_text = error_message()
rollback
return
end catch
commit
end
Now You will get eventually error from the output parameter #error_text
declare #error_text nvarchar(400)
exec dbo.InsertTable 'Value1','Value2', #error_text output
select #error_text
I have a larger stored procedure which utilizes several TRY/CATCH blocks in order to catch and log individual errors. I have also wrapped a transaction around the entire contents of the procedure, so as to be able to roll back the entire thing in the event of an error raised somewhere along the way (in order to prevent a lot of messy cleanup); XACT_ABORT has been enabled since it would otherwise not roll back the entire transaction.
Key component:
There is a table in my database which gets a record inserted each time this procedure is run with the results of operations and details on what went wrong.
Funny thing is happening - actually, when I finally figured out what was wrong, it was pretty obvious... the the insert statement into my log table is getting rolled back as well, hence, if I am not running this out of SSMS, I will not be able to see that this was even run, as the rollback removes all trances of activity.
Question:
Would it be possible to have the entire transaction roll back with the exception of this single insert statement? I would still want to preserve the error message which I compile during the running of the stored procedure.
Thanks so much!
~Eli
Update 6/28
Here's a code sample of what I'm looking at. Key difference between this and the samples posed by #Alex and #gameiswar is that in my case, the try/catch blocks are all nested inside the single transaction. The purpose of this is to have multiple catches (for the multiple tables), though we would the entire mess to be rolled back even if the last update failed.
SET XACT_ABORT ON;
BEGIN TRANSACTION
DECLARE #message AS VARCHAR(MAX) = '';
-- TABLE 1
BEGIN TRY
UPDATE TABLE xx
SET yy = zz
END TRY
BEGIN CATCH
SET #message = 'TABLE 1 '+ ERROR_MESSAGE();
INSERT INTO LOGTABLE
SELECT
GETDATE(),
#message
RETURN;
END CATCH
-- TABLE 2
BEGIN TRY
UPDATE TABLE sss
SET tt = xyz
END TRY
BEGIN CATCH
SET #message = 'TABLE 2 '+ ERROR_MESSAGE();
INSERT INTO LOGTABLE
SELECT
GETDATE(),
#message
RETURN;
END CATCH
COMMIT TRANSACTION
You can try something like below ,which ensures you log the operation.This takes advantage of the fact that table variables dont get rollbacked..
Psuedo code only to give you idea:
create table test1
(
id int primary key
)
create table logg
(
errmsg varchar(max)
)
declare #errmsg varchar(max)
set xact_abort on
begin try
begin tran
insert into test1
select 1
insert into test1
select 1
commit
end try
begin catch
set #errmsg=ERROR_MESSAGE()
select #errmsg as "in block"
if ##trancount>0
rollback tran
end catch
set xact_abort off
select #errmsg as "after block";
insert into logg
select #errmsg
select * from logg
OK... I was able to solve this using a combination of the great suggestions put forth by Alex and GameisWar, with the addition of the T-SQL GOTO control flow statement.
The basic ideas was to store the error message in a variable, which survives a rollback, then have the Catch send you to a FAILURE label which will do the following:
Rollback the transaction
Insert a record into the log table, using the data from the aforementioned variable
Exit the stored procedure
I also use a second GOTO statement to make sure that a successful run will skip over the FAILURE section and commit the transaction.
Below is a code snippet of what the test SQL looked like. It worked like a charm, and I have already implemented this and tested it (successfully) in our production environment.
I really appreciate all the help and input!
SET XACT_ABORT ON
DECLARE #MESSAGE VARCHAR(MAX) = '';
BEGIN TRANSACTION
BEGIN TRY
INSERT INTO TEST_TABLE VALUES ('TEST'); -- WORKS FINE
END TRY
BEGIN CATCH
SET #MESSAGE = 'ERROR - SECTION 1: ' + ERROR_MESSAGE();
GOTO FAILURE;
END CATCH
BEGIN TRY
INSERT INTO TEST_TABLE VALUES ('TEST2'); --WORKS FINE
INSERT INTO TEST_TABLE VALUES ('ANOTHER TEST'); -- ERRORS OUT, DATA WOULD BE TRUNCATED
END TRY
BEGIN CATCH
SET #MESSAGE = 'ERROR - SECTION 2: ' + ERROR_MESSAGE();
GOTO FAILURE;
END CATCH
GOTO SUCCESS;
FAILURE:
ROLLBACK
INSERT INTO LOGG SELECT #MESSAGE
RETURN;
SUCCESS:
COMMIT TRANSACTION
I don't know details but IMHO general logic can be like this.
--set XACT_ABORT ON --not include it
declare #result varchar(max) --collect details in case you need it
begin transaction
begin try
--your logic here
--if something wrong RAISERROR(...#result)
--everything OK
commit
end try
begin catch
--collect error_message() and other into #result
rollback
end catch
insert log(result) values (#result)
I've got this simple SPROC:
BEGIN
-- SET NOCOUNT ON added to prevent extra result sets from
-- interfering with SELECT statements.
SET NOCOUNT ON;
IF EXISTS(SELECT UserName FROM Party WHERE UserName = #UserName)
BEGIN
--This means it exists, return it to ASP and tell us
SELECT 'This record already exists!'
END
ELSE
BEGIN
--This means the record isn't in there already, let's go ahead and add it
SELECT 'Record Added'
-- Insert statements for procedure here
INSERT INTO Party
(EmailAddress, UserName, LoginPin)
VALUES (#EmailAddress, #UserName, #LoginPin)
END
END
How do I throw an exception from the SPROC so that my .NET C# app can catch the error using a TRY CATCH block?
You would use RAISERROR in your stored procedure.
See this Microsoft article on the topic.
Also, see the MSDN reference on RAISERROR