looping through all databases to find dependencies (cursor)

  • I want to have a go-to query that finds all dependencies of an object, across all databases (without requiring any permanent object like a stored procedure in the host server)

    I believe a cursor is one way to go, and below my cursor does loop through all databases and applies the query between BEGIN/END clause, per iteration.  Problem is that the database context I am in trumps the database identified per iteration.  Rather than finding dependencies in each db, it returns the current context's list of dependencies, appended with a different database name.

    What are some ideas for changing database context for each loop?


    DECLARE @tbl as varchar(100)

    SET @tbl = '%dbo.SHIPMENTS_Claims %'

    DECLARE @database as varchar(100)

    --get list of databases

    DECLARE database_cursor CURSOR LOCAL FOR

    SELECT name

    FROM Master.dbo.sysdatabases

    order by name asc

    OPEN database_cursor

    FETCH NEXT FROM database_cursor INTO @database

    WHILE @@FETCH_STATUS = 0

    BEGIN

    ----how to specify database context?  unable to use USE & GO here

    --SELECT @SqlCommand = 'USE [' + @database + ']' + CHAR(13) + 'GO'

    --EXEC (@SqlComman

    SELECT

    DISTINCT @database + '.dbo.' + a.[name] as ObjectName,

    CASE

    WHEN a.xtype = 'V' then 'View'

    WHEN a.xtype = 'P' then 'SPROC'

    ELSE a.xtype END as ObjectType

    FROM
    --or what to join on so that the database context is changed?

    sysobjects a INNER JOIN

    syscomments b on

    a.id = b.id

    WHERE b

    .[text] LIKE @tbl 

    FETCH NEXT FROM database_cursor INTO @database

    END

    CLOSE database_cursor

    DEALLOCATE database_cursor


    --Quote me

  • you need to query sysobjects (and other system dbs) in that database.

    as in select * from mydatbase.sys.objects.

  • And this will require that you use dynamic SQL to accomplish this task.

  • The easiest and cleanest way to do this would be to create stored proc in the master db, start the name with "sp_", mark it as a system object, and then use that one proc against all your user databases.

    But you don't have to do that.  You can use code like you have now.  Note that a "GO" is not required after a USE <db_name> (not since SQL 2000).  So just drop the "GO" from your code.

    Also, you can use view sys.sql_expression_dependencies to help you find object references within non-dynamic db code.  Of course there's no easy way to find object references within dynamic SQL.

    SQL DBA,SQL Server MVP(07, 08, 09) "It's a dog-eat-dog world, and I'm wearing Milk-Bone underwear." "Norm", on "Cheers". Also from "Cheers", from "Carla": "You need to know 3 things about Tortelli men: Tortelli men draw women like flies; Tortelli men treat women like flies; Tortelli men's brains are in their flies".

  • Steve Jones - SSC Editor - Wednesday, October 10, 2018 3:28 PM

    you need to query sysobjects (and other system dbs) in that database.

    as in select * from mydatbase.sys.objects.

    Hi Steve, can you show me how to modify the join?
    I tried qualifying the sys.objects view with the database name but query still only returns objects for the database in context.

    SELECT

    DISTINCT a.[name] as ObjectName,

    CASE

    WHEN a.type = 'V' then 'View'

    WHEN a.type = 'P' then 'SPROC'

    ELSE a.type END as ObjectType, *

    FROM nameofdatabase.sys.objects a INNER JOIN

    syscomments b on

    a.object_id = b.id

    WHERE

    b.[text] LIKE '%shipments%'

    --Quote me

  • As Lynn mentioned, dynamic SQL is needed.

    declare @cmd varchar(max)
    -- set cursor
    set @cmd = 'select ... from ' + @dbame + '.sys.objects'
    -- stuff
    exec(@cmd)

  • Steve Jones - SSC Editor - Thursday, October 11, 2018 8:02 AM

    As Lynn mentioned, dynamic SQL is needed.

    declare @cmd varchar(max)
    -- set cursor
    set @cmd = 'select ... from ' + @dbame + '.sys.objects'
    -- stuff
    exec(@cmd)

    or just use a tool... 

    I hear there's a really nice one...  I believe it's called SQL Dependency tracker.

    (Pretty sure Steve had heard of it 🙂 )

    ----------------------------------------------------------------------------------
    Your lack of planning does not constitute an emergency on my part...unless you're my manager...or a director and above...or a really loud-spoken end-user..All right - what was my emergency again?

  • You can't rely on syscomments for this.  The text column is nvarchar(4000), so large procedures can be split into multiple syscomments rows.  If your target text is split on a 4000-char boundary, the LIKE won't find it in either row.

    Use sys.sql_modules.definition (NVARCHAR(MAX)) instead.

    And you'll have to use dynamic SQL, but not necessarily a cursor.

  • I've adopted all suggestions for my query statement except for sys.sql_modules.definition since I need to see how to join to that instead of sys.comments.  Right now just trying to get the cursor working.  Still the query will not use the context of the database given by the cursor. What is wrong with the below query?

    DECLARE @tbl as varchar(100),

    @cmd as varchar(max),

    @database as varchar(100)

    SET @tbl = '%dbo.SHIPMENTS_Claims %'

    --get list of databases

    DECLARE database_cursor CURSOR LOCAL FOR

    SELECT name FROM Master.sys.databases order by name asc

    OPEN database_cursor

    FETCH NEXT FROM database_cursor INTO @database

    WHILE @@FETCH_STATUS = 0

    BEGIN

    set @cmd = ' use ' + @database +

    ' SELECT

    DISTINCT a.name as ObjectName,

    CASE

    WHEN a.xtype = ''V'' then ''View''

    WHEN a.xtype = ''P'' then ''SPROC''

    ELSE a.xtype END as ObjectType

    FROM ' + @database + '.sys.objects o INNER JOIN

    sysobjects a on o.object_id = a.id INNER JOIN

    syscomments b on a.id = b.id

    WHERE b.[text] LIKE '+ @tbl

    exec(@cmd)

    FETCH NEXT FROM database_cursor INTO @database

    END

    CLOSE database_cursor

    DEALLOCATE database_cursor

    --Quote me

  • polkadot - Sunday, October 14, 2018 1:59 PM

    I've adopted all suggestions for my query statement except for sys.sql_modules.definition since I need to see how to join to that instead of sys.comments.  Right now just trying to get the cursor working.  Still the query will not use the context of the database given by the cursor. What is wrong with the below query?

    DECLARE @tbl as varchar(100),

    @cmd as varchar(max),

    @database as varchar(100)

    SET @tbl = '%dbo.SHIPMENTS_Claims %'

    --get list of databases

    DECLARE database_cursor CURSOR LOCAL FOR

    SELECT name FROM Master.sys.databases order by name asc

    OPEN database_cursor

    FETCH NEXT FROM database_cursor INTO @database

    WHILE @@FETCH_STATUS = 0

    BEGIN

    set @cmd = ' use ' + @database +

    ' SELECT

    DISTINCT a.name as ObjectName,

    CASE

    WHEN a.xtype = ''V'' then ''View''

    WHEN a.xtype = ''P'' then ''SPROC''

    ELSE a.xtype END as ObjectType

    FROM ' + @database + '.sys.objects o INNER JOIN

    sysobjects a on o.object_id = a.id INNER JOIN

    syscomments b on a.id = b.id

    WHERE b.[text] LIKE '+ @tbl 

    exec(@cmd)

    FETCH NEXT FROM database_cursor INTO @database

    END

    CLOSE database_cursor

    DEALLOCATE database_cursor

    all tables need to refer to the same database


    FROM ' + @database + '.sys.objects o INNER JOIN

    ' + @database + '.sys.sysobjects a on o.object_id = a.id INNER JOIN

    ' + @database + '.sys.syscomments b on a.id = b.id

  • Thanks frederico_fonseca.    I made the correction.

    When running I am getting error   Msg 102, Level 15, State 1, Line 10 Incorrect syntax near 'dbo'.  Can you tell me why I am getting dbo error when I don't even use the dbo schema?


    DECLARE @tbl as varchar(100),

    @cmd as varchar(max),

    @database as varchar(100)

    SET @tbl = '%dbo.SHIPMENTS_Claims %'

    --get list of databases

    DECLARE database_cursor CURSOR LOCAL FOR

    SELECT name FROM Master.sys.databases order by name asc

    OPEN database_cursor

    FETCH NEXT FROM database_cursor INTO @database

    WHILE @@FETCH_STATUS = 0

    BEGIN

    set @cmd = ' use ' + @database + ' SELECT

    DISTINCT a.name as ObjectName,

    CASE

    WHEN a.xtype = ''V'' then ''View''

    WHEN a.xtype = ''P'' then ''SPROC''

    ELSE a.xtype END as ObjectType

    FROM ' + @database + '.sys.objects o INNER JOIN

    ' + @database + '.sysobjects a on o.object_id = a.id INNER JOIN

    ' + @database + '.syscomments b on a.id = b.id

    WHERE b.[text] LIKE '+ @tbl

    exec(@cmd)

    FETCH NEXT FROM database_cursor INTO @database
    END

    CLOSE database_cursor

    DEALLOCATE database_cursor

    --Quote me

  • You're using it in @tbl, but it's not escaped when you combine strings. Try printing the sql before running it.

  • andycadley - Monday, October 15, 2018 1:35 AM

    You're using it in @tbl, but it's not escaped when you combine strings. Try printing the sql before running it.

    Always do this. USe a SELECT @cmd rather than exec(@Cmd) and verify the code works as you expect.

  • thanks for the help, guys.  Cursor is working and I've incorporated sql_modules.  Final result below. 
    Is there a better way to do this that doesn't involve creating an object on the client's server or 3rd party software?  This cursor is VERY slow.

    DECLARE @tbl as varchar(100),

    @cmd as varchar(max),

    @database as varchar(100)

    SET @tbl = '%SHIPMENTS_Claims%'

    --get list of databases

    DECLARE database_cursor CURSOR LOCAL FOR

    SELECT name FROM Master.sys.databases order by name asc

    OPEN database_cursor

    FETCH NEXT FROM database_cursor INTO @database

    WHILE @@FETCH_STATUS = 0

    BEGIN

    set @cmd = ' use ' + @database +

    ' SELECT DISTINCT

    '''+ @database +''' as tablename,

    o.name as ObjectName,

    CASE o.xtype

    WHEN ''FN'' THEN ''FN''

    WHEN ''U'' THEN ''Table''

    WHEN ''V'' THEN ''View''

    WHEN ''P'' THEN ''SPROC'' ELSE o.xtype

    END as ObjectType

    FROM ' + @database + '.sys.sql_modules c INNER JOIN

    ' + @database + '.dbo.sysobjects o ON c.object_id = o.id LEFT JOIN

    ' + @database + '.dbo.sysobjects p ON o.Parent_obj=p.id

    WHERE c.definition LIKE '+ '''' + @tbl + ''''

    exec(@cmd)

    FETCH NEXT FROM database_cursor INTO @database

    END

    CLOSE database_cursor

    DEALLOCATE database_cursor

    --Quote me

  • A database is a database, and there isn't a way to search across them easily. Each is supposed to be self contained, and making joins across a large number of them isn't something that is architected in there.

    I would expect this to be slow, but I'd expect you wouldn't run this often. Is there a case here where you need to do this regularly?

Viewing 15 posts - 1 through 15 (of 15 total)

You must be logged in to reply to this topic. Login to reply