You used to get a warning message when you tried to create a stored procedure like that. It would say:
Cannot add rows to sysdepends for the current stored procedure because it depends on the missing object 'dbo.nonexistenttable'. The stored procedure will still be created.
For some reason I'm not getting it now, I'm not sure if it's been changed or if there's just some setting that turns the warning on or off. Regardless, this should give you a hint as to what's happening here.
SQL Server does track dependencies, but only dependencies which actually exist. Unfortunately, none of the dependency tricks like sp_depends
or sp_MSdependencies
will work here, because you're looking for missing dependencies.
Even if we could hypothetically come up with a way to check for these missing dependencies, it would still be trivial to concoct something to defeat the check:
CREATE PROCEDURE usp_Broken
AS
DECLARE @sql nvarchar(4000)
SET @sql = N'SELECT * FROM NonExistentTable'
EXEC sp_executesql @sql
You could also try parsing for expressions like "FROM xxx", but it's easy to defeat that too:
CREATE PROCEDURE usp_Broken2
AS
SELECT *
FROM
NonExistentTable
There really isn't any reliable way to examine a stored procedure and check for missing dependencies without actually running it.
You can use SET FMTONLY ON
as Tom H mentions, but be aware that this changes the way that the procedure is "run". It won't catch some things. For example, there's nothing stopping you from writing a procedure like this:
CREATE PROCEDURE usp_Broken3
AS
DECLARE @TableName sysname
SELECT @TableName = Name
FROM SomeTable
WHERE ID = 1
DECLARE @sql nvarchar(4000)
SET @sql = N'SELECT * FROM ' + @TableName
EXEC sp_executesql @sql
Let's assume you have a real table named SomeTable
and a real row with ID = 1
, but with a Name
that doesn't refer to any table. You won't get any errors from this if you wrap it inside a SET FMTONLY ON/OFF
block.
That may be a contrived problem, but FMTONLY ON
does other weird things like executing every branch of an IF
/THEN
/ELSE
block, which can cause other unexpected errors, so you have to be very specific with your error-handling.
The only truly reliable way to test a procedure is to actually run it, like so:
BEGIN TRAN
BEGIN TRY
EXEC usp_Broken
END TRY
BEGIN CATCH
PRINT 'Error'
END CATCH
ROLLBACK
This script will run the procedure in a transaction, take some action on error (in the CATCH
), and immediately roll back the transaction. Of course, even this may have some side-effects, like changing the IDENTITY
seed if it inserts into a table (successfully). Just something to be aware of.
To be honest, I wouldn't touch this problem with a 50-foot pole.