sp_depends - Don’t stake your project maintenance
on it!
By
Bill Wunder
Dependencies
in each SQL Server database are stored in the table
dbo.sysdepends
in that database. The system stored procedure
sp_depends is a
simple stored procedure that reads and formats data from this
table for us. So when you have to change a column attribute it
seems reasonable to run
sp_depends to
get a list of the DDL objects that might be affected or even
broken after your change. Sounds simple enough, but as anyone
that has depended on
sp_depends knows, it’s not good enough. This
article will look a little closer at
sp_depends, the
problem it pretends to help you solve, and also explore some of
the other alternatives at your disposal to identify
dependencies.
sysdepends
is populated at parse time when a stored procedure, trigger,
table or view is “compiled”. It is important to note that
objects are parsed only at the time they are created. With
SQL2000, If a procedure is parsed and an object from the local
database that is referenced by the procedure does not exist in
the database the parse does not fail, however there is a warning
message issued that the row could not be added to
sysdepends. If
sp_depends
is executed after this procedure is parsed into the database or
even after the table is created it will not see the dependency
until the procedure is parsed again. (i.e. drop procedure,
create procedure)
Consider
this demo of sp_depends misbehavior. Just cut-n-paste into your
Query Analyzer. Note that all comments are included in the
output, so if you run the script and review the output you'll
most likely understand what is happening better than if you just
read the script.
use
tempdb
print
'create a table'
create table SomeTable (id int)
print 'create a procedure that references this table and another
table'
go
create procedure ParseDemo
as
select id from SomeTable
select id from SomeOtherTable
go
print
'verify that the existing table shows up as a dependency'
print 'but the other one does not'
exec sp_depends SomeTable
exec sp_depends ParseDemo
go
print
'note that the non existent table causes an error'
print 'when passed to sp_depends'
exec sp_depends SomeOtherTable
go
print
'now create the other table and repeat the dependency check'
create table SomeOtherTable (id int)
go
print
'note that the dependency still does not show up'
print 'but the sp_depends that failed works now'
exec sp_depends SomeTable
exec sp_depends SomeOtherTable
exec sp_depends ParseDemo
go
print
'you cant even recompile the stored procedure'
print 'and get it to see the dependency'
exec sp_recompile ParseDemo
exec ParseDemo
go
exec
sp_depends SomeTable
exec sp_depends SomeOtherTable
exec sp_depends ParseDemo
go
print
'but if the procedure that is a phantom dependency'
print 'is dropped and recreated now that both tables exist'
print 'sp_depends shows the missing dependency'
drop procedure ParseDemo
go
create procedure ParseDemo
as
select id from SomeTable
select id from SomeOtherTable
go
exec
sp_depends SomeTable
exec sp_depends SomeOtherTable
exec sp_depends ParseDemo
go
print
'end of demo cleanup'
drop procedure ParseDemo
drop table SomeTable
drop table SomeOtherTable
go
Even
without this limitation that a dependency is not catalogued if
the object it depends upon does not exist, there are major
limitations to using
sp_depends. For example:
-
Triggers
attached to a table may not be considered when looking for
dependencies for that table. This is discussed in the
Microsoft Knowledge Base
article
Q180490.
-
Dependencies in dynamic SQL (EXEC()
, sp_execitesql
or even osql
via xp_cmdshell)
are not cataloged in
sysdepends.
Any time a string is built and then dynamically executed in
a stored procedure no dependencies to objects referenced in
the string will be reported.
-
Dependencies in OPENQUERY and OPENROWSET calls are not
cataloged in
sysdepends.
-
Dependencies in other databases are not cataloged in
sysdepends.
-
Dependencies on other SQL Server instances are not cataloged
in sysdepends.
-
Dependencies in embedded SQL application client code or
script file dependencies are not cataloged in
sysdepends.
-
Under some
circumstances, If the data from a table is moved into a new
table with the same name, the new table will have a new
identifier. The parser stores the identifier not the name in
the query plan. The dependency may not be properly cataloged
in sysdepends.
You get the
idea, right? sp_depends
can lead you down the garden path only to feed you to the
crocodiles. Finding the dependencies of an object will require
an exhaustive search. You'll want to check the syscomments table
in all databases on all SQL Server instances used by an
application as well as the application source code. GREP, PERL,
SourceSafe’s “Find in Files” option, or any good tool with
similar functionality can be used for source files. In
SourceSafe you could also look for dependencies external to SQL
Server as well as application source code. Obviously, the
limitation here is that if it didn't get checked in to
SourceSafe you won’t find a dependency. Looking for a string
match in the syscomments table seems to be most effective inside
a SQL Server database.
Something
like:
select object_name(id) from
dbo.syscomments where text like '%SomeTable%'
You can do
this using the
sp_MSforeachdb stored procedure to cover all
databases on a server. For example:
exec sp_msforeachdb 'use ?
select db_name() + ''.'' + object_name(id)
from syscomments
where text like ''%SomeTable%'''
If you have more than one server to check you could run this once on each server or you could build a procedure that looks on all servers. Such as:
CREATE PROCEDURE dbo.find_dependancies
@SearchString nvarchar(255)
as
declare @LinkedServer nvarchar(20),
@SearchQuery nvarchar(4000)
set nocount on
-- all other servers will need a linked server defined and a hard coded entry here
if charindex('SERVER_A',@@servername) = 0
and charindex('SERVER_B',@@servername) = 0
and charindex('SERVER_C',@@servername) = 0
begin
raiserror('find_dependancies cannot be executed on this server',16,1)
return -1
end
else -- local server always has a linked server defined for itself with the same name as the server
set @LinkedServer = @@servername
while @LinkedServer is not null
begin
print 'Checking for string ' + @SearchString
+ ' on server ' + @LinkedServer
set @SearchQuery = 'set nocount on exec ' + @LinkedServer
+ '.master.dbo.sp_msforeachdb ''set nocount on use ? '
+ 'if exists (select 1 from syscomments where text like ''''%'
+ @SearchString + '%'''') select distinct @@servername
+ ''''.'''' + db_name() + ''''.''''
+ object_name(id) from syscomments where text like ''''%'
+ @SearchString + '%'''''''
exec sp_executesql @SearchQuery
if @LinkedServer = 'SERVER_A'
set @LinkedServer = 'SERVER_B'
else if @LinkedServer = 'SERVER_B'
set @LinkedServer = 'SERVER_C'
else
set @LinkedServer = null -- done
end
This will
produce a list of all objects with the literal that you passed.
You could even get fancier with the linked servers and make sure
they are in
master.dbo.sysservers, or available through
sp_linkedservers,
or even from a table. Lots of possibilities here, it all boils
down to what makes the most sense for your situation. From any
of the source file or syscomments parsing results you will still
need to review each procedure, trigger, or reference listed to
verify that it is a real dependency not simply a comment or
coincidence. If you look for "SomeTable" the result could
produce a match on "LonesomeTableBuzzard". Sorry, finding
dependencies is a pretty manual process in the real world.
Within SQL
Server you’ll still want to keep an eye out for rules and even
permissions that might be affected by the change you are
considering. The reality is that finding dependencies in SQL
server can be fraught with misinformation. In the end, the
developer must do exhaustive and even redundant investigative
work to properly identify all dependencies.
And for
sure, don't depend on
sp_depends.
Bill
|