查看數據庫對象的引用關系,查看數據庫對象的依賴關系


轉自:https://www.cnblogs.com/seusoftware/p/4858115.html

在SQL Server中,(可編程)對象間的引用即依賴關系,有多種方式可以檢查,隨着版本變更,方式也有所不同。

父子關系的對象,不通過依賴關系來查詢,比如:

1. 外鍵關系

復制代碼
use tempdb
GO
--drop table tb1,tb2
create table tb1
(
col1 int Primary key,
col2 int
)
insert into tb1 values (2,2),(3,2),(4,2),(5,2)
GO
create table tb2
(
col3 int primary key,
col4 int constraint FK_tb2 foreign key  references tb1(col1)
)
GO
--檢查外鍵
select object_name(constraint_object_id) constraint_name,
       object_name(parent_object_id) parent_object_name,
       col_name(parent_object_id,parent_column_id) parent_object_column_name,
       object_name(referenced_object_id) referenced_object_name,
       col_name(referenced_object_id,referenced_column_id) referenced_object_column_name
 from sys.foreign_key_columns
where referenced_object_id = object_id('tb1')
復制代碼

2. 表上的索引,觸發器

復制代碼
use tempdb
GO
if OBJECT_ID('T','U') is not null
    drop table T
create table T(id int)
GO

if exists(select 1 from sys.indexes where name = 'IX_001' and object_id = object_id('T','U'))
    drop index T.IX_001
create index IX_001 on T(id)

if OBJECT_ID ('test_dml_trigger', 'TR') is not null
   drop trigger test_dml_trigger
GO
create trigger test_dml_trigger
ON T
AFTER INSERT, UPDATE 
AS 
RAISERROR ('Notify Customer Relations', 16, 10);
GO

--檢查索引
select object_name(object_id) as table_name,* 
from sys.indexes 
where name = 'IX_001' and object_id = object_id('T','U')

--檢查DML觸發器
select name as table_name, object_name(a.parent_obj) as dml_trigger_name
from sysobjects a
where a.xtype = 'TR'
復制代碼

 

在SSMS中,數據庫對象上右擊/View Dependencies,可以查看到對象的依賴關系,那么用腳本怎么檢查?

復制代碼
create database DB1;
create database DB2;

use DB1
GO
if OBJECT_ID('T1','U') is not null
    drop table T1
GO
create table T1(id int);
GO

if OBJECT_ID('V1','V') is not null
    drop view V1
GO
create view V1
as 
select * from T1
GO

if OBJECT_ID('SP1','P') is not null
    drop proc SP1
GO
create proc SP1
as
select * from V1
GO

use DB2
GO
if OBJECT_ID('SP2','P') is not null
    drop proc SP2
GO
create proc SP2
as
select * from DB1..V1
GO

use DB1
GO
if OBJECT_ID('SP3','P') is not null
    drop proc SP3
GO
create proc SP3
as
exec DB2..SP2
GO

use DB1
GO
if object_id('test_schema.T2','U') is not null
    drop table test_schema.T2
GO
if exists(select 1 from sys.schemas where name  = 'test_schema')
    drop schema test_schema
GO
create schema test_schema
create table test_schema.T2(c1 int, c2 int)
GO

if OBJECT_ID('SP4','P') is not null
    drop proc SP4
GO
create proc SP4
as
select * from test_schema.T2
GO
復制代碼

 

. SQL Server 2000依賴關系查詢

復制代碼
--從SQL Server 2000沿用下來的系統表,SQL Server 2016仍適用,后續版本將不再支持
USE DB1
SELECT o.name, o.xtype, p.name as referenced_name, p.xtype
FROM sysdepends d
INNER JOIN sysobjects o
    ON d.id = o.id
INNER JOIN sysobjects p
    ON d.depid = p.id

--從SQL Server 2000沿用下來的存儲過程,SQL Server 2016仍適用,后續版本將不再支持
USE DB1
exec sp_depends 'V1'

--無文檔記載的sp_MS存儲過程,只能檢查被自己引用的對象,SQL Server 2016仍適用
exec sp_MSdependencies 'V1'
復制代碼

注意:sysdepends, sp_depends, sp_MSdependencies 只能檢查當前數據庫對象的引用/被引用,對於跨數據庫對象依賴關系,無法檢查。

 

. SQL Server 2005依賴關系查詢

復制代碼
--從SQL Server 2005沿用下來的系統視圖,SQL Server 2016仍適用,后續版本將不再支持
USE DB1
SELECT o.name, o.type_desc, p.name as referenced_name, p.type_desc
FROM sys.sql_dependencies d
INNER JOIN sys.objects o
    ON d.object_id = o.object_id
INNER JOIN sys.objects p
    ON d.referenced_major_id = p.object_id
復制代碼

注意:和sysdepends, sp_depends一樣,sys.sql_dependencies只能檢查當前數據庫對象的引用/被引用,對於跨數據庫對象依賴關系,無法檢查。

 

. SQL Server 2008后依賴關系查詢

復制代碼
--從SQL Server 2008開始用的系統視圖
USE DB1
SELECT o.name, o.type_desc, p.name as referenced_name, p.type_desc
FROM sys.sql_expression_dependencies d
INNER JOIN sys.objects o
    ON d.referencing_id = o.object_id
INNER JOIN sys.objects p
    ON d.referenced_id = p.object_id

USE DB1
--從SQL Server 2008開始用的系統函數,引用我的對象
SELECT * FROM sys.dm_sql_referencing_entities('dbo.V1','OBJECT')
--從SQL Server 2008開始用的系統函數,被我引用的對象
SELECT * FROM sys.dm_sql_referenced_entities('dbo.SP1','OBJECT')

USE DB2
--從SQL Server 2008開始用的系統函數,引用我的對象
SELECT * FROM sys.dm_sql_referencing_entities('dbo.SP2','OBJECT')
--從SQL Server 2008開始用的系統函數,被我引用的對象
SELECT * FROM sys.dm_sql_referenced_entities('dbo.SP2','OBJECT')
復制代碼

注意:

(1) sys.sql_expression_dependencies及這兩個新增函數,都可以檢查當前數據庫中跨數據庫,跨服務器引用的對象,但當前數據庫對象被跨數據庫,跨服務器引用,無法檢查;

(2) 新增的2個系統函數,可以更方便的檢查引用和被引用,但對象名要完整,必須包含schema name,否則無法返回正確結果;

(3) sys.dm_sql_referenced_entities 還可以查看被數據庫/服務器DDL觸發器引用的對象;

SELECT * FROM sys.dm_sql_referenced_entities ('ddl_database_trigger_name', 'DATABASE_DDL_TRIGGER');

(4) sys.dm_sql_referencing_entities 還可以查看引用了類型/分區函數等的對象。

 

無法查明的依賴關系

1. 跨數據庫/服務器對象

上面提到從SQL Server 2008開始,跨數據庫,跨服務器引用的對象,已經可以查詢;

但是寫法上要稍微調整下,因為當前數據庫中,並沒有其他數據庫對象的object_id,所以不能按照object_id來關聯。改動后腳本如下:

復制代碼
USE DB1
SELECT schema_name(o.schema_id) as schema_name, o.name as object_name, o.type_desc, 
       d.referenced_server_name, d.referenced_database_name, isnull(d.referenced_schema_name,'dbo') as referenced_schema_name, d.referenced_entity_name
FROM sys.sql_expression_dependencies d
INNER JOIN sys.objects o
    ON d.referencing_id = o.object_id
復制代碼

注意:跨數據庫/跨服務器對象的引用,僅能檢查3部分/4部分名稱格式的對象引用,即如:server_name.db_name.schema_name.object_name格式,對於OPENROWSET, OPENQUERY, OPENDATASOURCE的引用並不記錄。

 

2. 臨時對象

對於存儲過程中用到的臨時表,只能檢查到create table創建的非#開頭臨時表,並且用函數檢查還會報錯,因為表事先並不存在。

復制代碼
if OBJECT_ID('SP5','P') is not null
    drop proc SP5
GO
create proc SP5
as
select * into #temp from sys.objects
select * into _temp from sys.objects
select getdate()

create table #t (id int)
insert into #t select 100

if OBJECT_ID('_t','U') is not null
    drop proc _t
create table _t (id int)
insert into _t select 100
GO
復制代碼
復制代碼
USE DB1
--只能檢查到create table創建的非#臨時表
SELECT schema_name(o.schema_id) as schema_name, o.name as object_name, o.type_desc, 
       d.referenced_server_name, d.referenced_database_name, isnull(d.referenced_schema_name,'dbo') as referenced_schema_name, d.referenced_entity_name
FROM sys.sql_expression_dependencies d
INNER JOIN sys.objects o
    ON d.referencing_id = o.object_id

--並且用函數檢查還會報錯,因為表事先並不存在
select * from sys.dm_sql_referenced_entities('dbo.SP5','OBJECT');
/*
Msg 2020, Level 16, State 1, Line 4
The dependencies reported for entity "dbo.SP5" might not include references to all columns. This is either because the entity references an object that does not exist or because of an error in one or more statements in the entity.  Before rerunning the query, ensure that there are no errors in the entity and that all objects referenced by the entity exist.
*/
復制代碼

 

3. 動態SQL里引用的對象

復制代碼
use DB1
GO
if OBJECT_ID('T2','U') is not null
    drop table T2
GO
create table T2(id int);
GO
if OBJECT_ID('SP6','P') is not null
    drop proc SP6
GO
create proc SP6
as
exec('select * from T1')

declare @SQL nvarchar(max)
set @SQL = N'select * from T2'
exec sp_executesql @SQL
exec (@SQL)
GO
復制代碼
復制代碼
USE DB1
--無論系統視圖/函數,都查不到
SELECT schema_name(o.schema_id) as schema_name, o.name as object_name, o.type_desc, 
       d.referenced_server_name, d.referenced_database_name, isnull(d.referenced_schema_name,'dbo') as referenced_schema_name, d.referenced_entity_name
FROM sys.sql_expression_dependencies d
INNER JOIN sys.objects o
    ON d.referencing_id = o.object_id

--無論系統視圖/函數,都查不到
select * from sys.dm_sql_referenced_entities('dbo.SP6','OBJECT');
復制代碼

動態SQL里引用的對象,無論系統視圖/函數,都查不到;也許只能試試查可編程對象的文本定義:

復制代碼
--ANSI SQL標准里定義的INFORMATION_SCHEMA對象
select * from INFORMATION_SCHEMA.ROUTINES 
where ROUTINE_DEFINITION like '%T2%'

--SQL Server 2000沿用下來的可編程對象文本定義
select * from syscomments
where text like '%T2%'

--SQL Server 2005開始的可編程對象文本定義
select * from sys.sql_modules 
where definition like '%T2%'
復制代碼

 

注意:這種方法,對於hard coding的對象名,非常好用,但是,

(1) 有時動態SQL里的對象名稱並不是hard coding,所以也不一定能找到;比如:

EXEC('SELECT * FROM dbo.table' + '_name') 
EXEC('SELECT * FROM ' + @table)

(2) 另外一些書寫不嚴格的SQL,也無法定位到對象名,比如:

SELECT * FROM dbo . table_name --這語法竟然也能通過
SELECT * FROM dbo.table_name_2 --名字只是部分類似,table_name_2不是table_name

 

4. 延遲名稱解析

如果被引用的數據庫對象,在后面創建,那么用2000或者2005的方式去檢查,會出現延遲名稱解析(deferred name resolution),用2008后的方式,已經沒有這個問題。

復制代碼
use DB2
GO
if OBJECT_ID('T3','U') is not null
    drop table T3
GO
create table T3(id int);
GO

--引用的SP_1st后創建
if OBJECT_ID('SP_2nd','P') is not null
    drop proc SP_2nd
GO
create proc SP_2nd
as
exec SP_1st
GO

if OBJECT_ID('SP_1st','P') is not null
    drop proc SP_1st
GO
create proc SP_1st
as
select * from T3
GO
復制代碼
復制代碼
--出現延遲名稱解析(deferred name resolution): 存儲過程SP_2nd的引用對象,無法獲取到
USE DB2
SELECT o.name, o.xtype, p.name as referenced_name, p.xtype
FROM sysdepends d
INNER JOIN sysobjects o
    ON d.id = o.id
INNER JOIN sysobjects p
    ON d.depid = p.id

exec sp_depends 'SP_1st'
exec sp_depends 'SP_2nd'

USE DB2
SELECT o.name, o.type_desc, p.name as referenced_name, p.type_desc
FROM sys.sql_dependencies d
INNER JOIN sys.objects o
    ON d.object_id = o.object_id
INNER JOIN sys.objects p
    ON d.referenced_major_id = p.object_id

--刷新對象定義,可以解決
exec sp_refreshsqlmodule 'SP_2nd'
--如果是視圖,也可以這樣刷新
exec sp_refreshview 'view_name'

--使用2008后的系統視圖,沒有這個問題,它同時保存了引用對象的名稱,object_id可先置為NULL
USE DB2
SELECT schema_name(o.schema_id) as schema_name, o.name as object_name, o.type_desc, 
       d.referenced_server_name, d.referenced_database_name, isnull(d.referenced_schema_name,'dbo') as referenced_schema_name, d.referenced_entity_name
FROM sys.sql_expression_dependencies d
INNER JOIN sys.objects o
    ON d.referencing_id = o.object_id
復制代碼

注意:新的視圖雖然解決了延遲名稱解析的問題,但也帶來了新問題,如果引用的對象一直未被創建,或者創建后被重名命/刪除,這條依賴關系仍然存在。

 

如何獲取多層嵌套引用的對象

有時一個對象下會多層嵌套引用數據庫對象,尤其是視圖/存儲過程等的嵌套調用,在某些場景下獲取所有嵌套調用的對象很有用,比如:要更新某個存儲過程下所有引用到的表上的統計信息。

復制代碼
use DB2
GO
create table dbo.table2(c2 int)
GO

create proc dbo.sp12
as
select * from table2
GO

use DB1
GO

create table dbo.table1(c1 int)
GO

create view dbo.view1 
as 
select * from dbo.table1
GO

create view dbo.view2 
as 
select * from dbo.view1
GO

create proc dbo.sp11
as
select * from dbo.view2
GO

create proc dbo.sp13
as 
exec dbo.sp11
exec DB2.dbo.sp12
GO
復制代碼
復制代碼
use DB2
GO
declare @entity_name varchar(512)
set @entity_name = 'dbo.sp13'

;with tmp
as
(
SELECT *
FROM sys.sql_expression_dependencies d
WHERE d.referencing_id = object_id(@entity_name)
union all
SELECT d.*
FROM sys.sql_expression_dependencies d
INNER JOIN tmp t
   ON t.referenced_id = d.referencing_id
)
--select * from tmp
SELECT schema_name(o.schema_id) as schema_name, o.name as object_name, o.type_desc, 
       d.referenced_server_name, d.referenced_database_name, isnull(d.referenced_schema_name,'dbo') as referenced_schema_name, d.referenced_entity_name
  FROM tmp d
 INNER JOIN sys.objects o
    ON d.referencing_id = o.object_id
 -- LEFT JOIN sys.objects ro
 --   ON d.referenced_id = ro.object_id
 --WHERE ro.type_desc = 'USER_TABLE' or ro.type_desc is null
復制代碼

注意:

(1) 最后注釋的幾行腳本,限制用來獲取所有被引用到的表,可根據需要調整;

(2) 跨數據庫/服務器引用的對象,如果不是最后一層,還得切換到對應的數據庫/服務器再運行此腳本。

 

小結:

1. 查看被哪些對象引用,sys.sql_expression_dependencies,sys.dm_sql_referencing_entities, sys.sql_modules,無論哪種方式都查不到被跨數據庫引用;

2. 查看引用了哪些對象,sys.sql_expression_dependencies,sys.dm_sql_referenced_entities,都可以查到跨數據庫引用的對象,如果查看嵌套調用的對象,還是遞歸查詢sys.sql_expression_dependencies比較直接。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM