SQL: Try/Catch doesn't catch an error when attempting to access a table that it can't find
At the start of your script use SET XACT_ABORT
SET XACT_ABORT ON
When SET XACT_ABORT is ON, if a Transact-SQL statement raises a
run-time error, the entire transaction is terminated and rolled back.
I don't think that's going to be possible:
The following types of errors are not handled by a CATCH block when
they occur at the same level of execution as the TRY…CATCH construct:
Compile errors, such as syntax errors, that prevent a batch from running.
Errors that occur during statement-level recompilation, such as object name resolution errors that occur after compilation because of
deferred name resolution.
Ref.
The following example shows how an object name resolution error
generated by a SELECT statement is not caught by the TRY…CATCH
construct, but is caught by the CATCH block when the same SELECT
statement is executed inside a stored procedure.
USE AdventureWorks2012;
GO
BEGIN TRY
-- Table does not exist; object name resolution
-- error not caught.
SELECT * FROM NonexistentTable;
END TRY
BEGIN CATCH
SELECT
ERROR_NUMBER() AS ErrorNumber
,ERROR_MESSAGE() AS ErrorMessage;
END CATCH
The error is not caught and control passes out of the TRY…CATCH
construct to the next higher level.
TRY/CATCH not working when table goes missing
Based on the information provided it looks like it may be a problem with your syntax, but it is unclear without a CREATE TABLE statement and some working code. It could also be that you're not checking if the table exists before the SELECT. I just tested the below and it has the desired results.
BEGIN TRY
BEGIN TRAN
TRUNCATE TABLE [test_table]
INSERT INTO [test_table] VALUES ('...')
SELECT * FROM [test_table]
COMMIT
END TRY
BEGIN CATCH
IF (@@TRANCOUNT > 0)
PRINT N'rolling back transaction' --for confirmation of the catch
ROLLBACK
END CATCH
Or to avoid the TRY/CATCH use IF EXISTS to check if the table exists before starting anything.
IF EXISTS (SELECT 1 FROM sys.tables WHERE name = 'test_table')
BEGIN
BEGIN TRAN
TRUNCATE TABLE [test_table]
INSERT INTO [test_table] VALUES ('...')
SELECT * FROM [test_table]
COMMIT
END
ELSE
BEGIN
-- do something else
PRINT N'The table does not exist'
END
Hope this helps!
Nested Try/Catches - Does Only Outer Catch Matter (MS SQL)
The answer is in @GarethD's comment. Adding here as the answer so that it can be found by the next victim. I didn't come across that question while searching.
SQL: Try/Catch doesn't catch an error when attempting to access a table that it can't find
GarethD, if you post an answer, I'll mark it as the answer.
SQL How to; Try-Catch error info to error-log table, for stored proc executed within a transaction?
Since transactions are an all or nothing deal, the only way to get done what I'm wanting done, is to call ROLLBACK TRANSACTION inside of the Catch block, before calling my error handling call.
My error log ends up with the error I want logged, but in my .Net catch block, I end up with a complaint from SQL: "Transaction count after EXECUTE indicates that a COMMIT or ROLLBACK TRANSACTION statement is missing. Previous count = 1, current count = 0."
I can live with that!
TRY CATCH Block in T-SQL
Barring the fact that the "line error occured on" part of any message returned would reference the RAISERROR line and not the line the error actually occured on, there will be no difference. The main reason to do this is as @Chris says, to allow you to programmatically use/manipulate the error data.
How do I catch a query exception in laravel to see if it fails?
The simplest way to catch any sql
syntax or query errors is to catch an Illuminate\Database\QueryException
after providing closure to your query:
try {
$results = \DB::connection("example")
->select(\DB::raw("SELECT * FROM unknown_table"))
->first();
// Closures include ->first(), ->get(), ->pluck(), etc.
} catch(\Illuminate\Database\QueryException $ex){
dd($ex->getMessage());
// Note any method of class PDOException can be called on $ex.
}
If there are any errors, the program will die(var_dump(...))
whatever it needs to.
Note: For namespacing, you need to first \
if the class is not included as a use
statement.
Also for reference:
Laravel 5.5 API - Query Exception
Laravel 8.x API - Query Exception
How do I catch a specific exception type from within a stored procedure?
You can't catch error 208 directly because it's a name resolution error that is raised at compilation time and before the code is actually executed. The behaviour is documented: see the section called "Errors Unaffected by a TRY…CATCH Construct" for an explanation, and the answers to this question have some interesting comments.
In addition to the 'solution' in the documentation, you can use dynamic SQL; the error will be caught in this example:
begin try
exec('select * from dbo.ThisTableDoesNotExist');
end try
begin catch
select error_number();
end catch;
If you're looping through all databases, there's a good chance you're using dynamic SQL somewhere anyway, so this might suit your case better.
Output parameter value changes depending on error type
I have been through several iterations of this answer, as I have found out more. I think I have now come to a conclusion.
As OP has pointed out, the non-return of the updated parameter is because of the copy-in/copy-out operation of the OUTPUT parameters. However, I have found that a non-existent table error appears not to be picked up by TRY-CATCH inside the SP, but is caught by TRY-CATCH outside.
The following is an amended version of the second example, using TRY..CATCH
to pinpoint where the error is picked up.
I used this code to run all variants of the SP:
DECLARE @v int, @result int = 0, @Flag int = 1;
SET @v = 1;
BEGIN TRY
SET @Flag = 2;
EXEC @Result = SP @v OUTPUT;
SET @Flag = 3;
END TRY
BEGIN CATCH
PRINT ' C: @Flag=' + CAST( @Flag AS Varchar(10) );
SET @Flag = 4;
END CATCH
PRINT ' D: @v=' + CAST( @v AS Varchar(10) );
PRINT ' E: @Result=' + CAST( @Result AS Varchar(10) );
PRINT ' F: @Flag=' + CAST( @Flag AS Varchar(10) );
The first version of the SP is:
ALTER PROC SP (@p1 int OUTPUT) AS
BEGIN
PRINT '>> SP';
SET @p1 = @p1 + 10;
PRINT ' A: @p1=' + CAST( @P1 as varchar(10) );
BEGIN
BEGIN TRY
SELECT * FROM nonExistentTable;
END TRY
BEGIN CATCH
SET @p1 = 999;
RETURN 22;
END CATCH;
RETURN 399;
END
PRINT ' B: @p1=' + CAST( @P1 as varchar(10) );
PRINT '<< SP';
END
The output is:
>> SP
A: @p1=11
C: @Flag=2
D: @v=1
E: @Result=0
F: @Flag=4
So it appears that, even though the output parameter @p1 is updated within the SP, the value of the parameter is not passed back to the caller. The non-existent table error is not trapped by the TRY-CATCH inside the SP and by-passes any updates to OUTPUT parameters, but it is caught by the TRY-CATCH outside the SP.
Now, having tested that, I added some additional code into the SP:
ALTER PROC SP (@p1 int OUTPUT) AS
BEGIN
PRINT '>> SP';
SET @p1 = @p1 + 10;
PRINT ' A: @p1=' + CAST( @P1 as varchar(10) );
IF (EXISTS (SELECT *
FROM INFORMATION_SCHEMA.TABLES
WHERE TABLE_SCHEMA = 'dbo'
AND TABLE_NAME = 'nonExistentTable'))
BEGIN
BEGIN TRY
SELECT * FROM nonExistentTable;
END TRY
BEGIN CATCH
SET @p1 = 999;
RETURN 22;
END CATCH;
RETURN 399;
END
PRINT ' B: @p1=' + CAST( @P1 as varchar(10) );
PRINT '<< SP';
END
with this result:
>> SP
A: @p1=11
B: @p1=11
<< SP
D: @v=11
E: @Result=0
F: @Flag=3
which is intriguing. So I changed the SP again, this time to select rows from a table that I know contains data:
ALTER PROC SP (@p1 int OUTPUT) AS
BEGIN
PRINT '>> SP';
SET @p1 = @p1 + 10;
PRINT ' A: @p1=' + CAST( @P1 as varchar(10) );
IF EXISTS( SELECT * FROM Data_Table )
BEGIN
BEGIN TRY
SELECT * FROM nonExistentTable;
END TRY
BEGIN CATCH
SET @p1 = 999;
RETURN 22;
END CATCH;
RETURN 399;
END
PRINT ' B: @p1=' + CAST( @P1 as varchar(10) );
PRINT '<< SP';
END
In this case the result is:
>> SP
A: @p1=11
C: @Flag=2
D: @v=1
E: @Result=0
F: @Flag=4
Finally a check on a completely unrelated table that is empty:
ALTER PROC SP (@p1 int OUTPUT) AS
BEGIN
PRINT '>> SP';
SET @p1 = @p1 + 10;
PRINT ' A: @p1=' + CAST( @P1 as varchar(10) );
IF EXISTS( SELECT * FROM Empty_Table )
BEGIN
BEGIN TRY
SELECT * FROM nonExistentTable;
END TRY
BEGIN CATCH
SET @p1 = 999;
RETURN 22;
END CATCH;
RETURN 399;
END
PRINT ' B: @p1=' + CAST( @P1 as varchar(10) );
PRINT '<< SP';
END
with the result:
>> SP
A: @p1=11
B: @p1=11
<< SP
D: @v=11
E: @Result=0
F: @Flag=3
Someone who knows more about how the internals of SQL Server work might be able to explain and, perhaps, justify these results.
Related Topics
Postgresql:How to Select Top N Percent(%) Entries from Each Group/Category
Create Temp Table with Range of Numbers
Performing a Where - in Query in Couchdb
Inserting a Variable in a Raw SQL Query Laravel
SQL - Pivot Table and Group by Not Working
Access SQL How to Make an Increment in Select Query
How to Properly Trigger an Insert to a Linked SQL Server
Update Columns with Null Values
Using Pivot to Flip Data from Wide to Tall
SQL Server Group by Query Select First Row Each Group
Limiting the Number of Records in a SQLite Db
How to List the Source Table Name of Columns in a View (SQL Server 2005)
Table Valued Function Where Did My Query Plan Go
How to Remove Elements of Array in Postgresql
How to Make a List of T-SQL Results with Comma's Between Them