查看数据库对象的引用关系,查看数据库对象的依赖关系

转自: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比较直接。

原文地址:https://www.cnblogs.com/gered/p/10812374.html

时间: 2024-10-15 02:17:46

查看数据库对象的引用关系,查看数据库对象的依赖关系的相关文章

为什么父类引用可以指向子类对象 子类引用不能指向父类对象 泛型

假设有父类Fu ,其子类为Zi ,从对象的内存角度,假设Fu类里面的变量占内存2M, Zi 类里的变量占内存1M: Fu f = new Fu ();//系统将分配2M内存 Zi z = new Zi ();//系统将分配3M内存(2+1) 因为子类中有一个隐藏的引用super会指向父类实例,所以在实例化子类之前会先实例化一个父类,也就是说会先执行父类的构造函数.所以z可以调用父类的方法. Zi z1= z; //z1指向那3M的内存. Fu f1 = (Fu)z;//这时f1会指向那3M内存中

性能优化 - 查看 webpack 打包后所有的依赖关系(webpack 可视化工具)

查看 webpack 打包后所有组件与组件间的依赖关系,针对多余的包文件过大, 剔除首次影响加载的效率问题进行剔除修改,本次采用的是 ==webpack-bundle-analyzer(可视化视图查看器)== == 介绍1:webpack-bundle-analyzer(可视化)== 将捆绑内容表示为方便的交互式可缩放树形图 如下效果图: 模块功能: 意识到你的文件打包压缩后中真正的内容 找出哪些模块组成最大的大小 找到错误的模块 优化它! 最好的事情是它支持缩小捆绑!它解析它们以获得实际大小的

Python 对象的引用计数和拷贝

Python 对象的引用计数和拷贝 Python是一种面向对象的语言,包括变量.函数.类.模块等等一切皆对象. 在python中,每个对象有以下三个属性: 1.id,每个对象都有一个唯一的身份标识自己,可通过内建函数id(obj)查看. 2.type,对象的类型决定了该对象可以保存什么类型的值,可用内建函数type(obj)查看: 3.value,即对象的值. 下面是一个例子: >>> str = "hello world" >>> type(str

Andorid Binder进程间通信---Binder本地对象,实体对象,引用对象,代理对象的引用计数

本文参考<Android系统源代码情景分析>,作者罗升阳. 一.Binder库(libbinder)代码: ~/Android/frameworks/base/libs/binder ----BpBinder.cpp ----Parcel.cpp ----ProcessState.cpp ----Binder.cpp ----IInterface.cpp ----IPCThreadState.cpp ----IServiceManager.cpp ----Static.cpp ~/Androi

C#中字符串的处理,对象的引用及继承(Tenth day)

又进入到了新的一周,现在到总结的时间了,继续为大家总结一下今天在云和学院所学的知识. 理论: StringBuilder 和 String 的区别 String 在进行运算时(如赋值.拼接等)会产生一个新的实例,而 StringBuilder 则不会.所以在大量字符串拼接或频繁对某一字符串进行操作时最好使用 StringBuilder,不要使用 String: 如果要操作一个不断增长的字符串,尽量不用String类,改用StringBuilder类.两个类的工作原理不同:String类是一种传统

java String不可变对象,但StringBuffer是可变对象

什么是不可变对象? 众所周知, 在Java中, String类是不可变的.那么到底什么是不可变的对象呢? 可以这样认为:如果一个对象,在它创建完成之后,不能再改变它的状态,那么这个对象就是不可变的.不能改变状态的意思是,不能改变对象内的成员变量,包括基本数据类型的值不能改变,引用类型的变量不能指向其他的对象,引用类型指向的对象的状态也不能改变. 区分对象和对象的引用 对于Java初学者, 对于String是不可变对象总是存有疑惑.看下面代码: [java] view plain copy Str

【译】Gradle 的依赖关系处理不当,可能导致你编译异常

文章 | Ashesh Bharadwaj 翻译 | 承香墨影 授权 承香墨影 翻译.编辑并发布 在 Android Studio 中,Gradle 构建过程对于开发者来说,很大程度上是抽象的.作为一个新的 Android 开发者,我们第一次遇到 Gradle 通常是在 build.gradle 文件中添加一个远程依赖项. 让我们看看如何阅读 Gradle 依赖关系树,并解决与依赖关系有关的问题. 这是我工作中管理的一个项目,我想将 targetVersion 升级到 27,我也在 Gradle

手动创建Maven项目并建立两个项目之间的依赖关系

用命令行快速建立maven项目 -> mvn:archetype:generate -> 直接回车或者自己输入你想生成的 -> groupId ->artifactId ->如果有默认值回车即可 最后 y 确认创建 我们看下他的目录结构 项目名: src ->main ->java ->test ->java pom.xml <project xmlns="http://maven.apache.org/POM/4.0.0"

Android注解使用之Dagger2实现项目依赖关系解耦

前言: 最近牵头发起公司app的重构工作,如何通过重构让项目的耦合降低.开发效率提高,一直是我努力的方向,今天来学习一下一个注解框架Dagger2,然后看看如何使用它来降低项目的耦合. Dagger2 一句话:一款快速的注解框架,应用于Android.Java,由 Google 开发和维护,是 Square 的 Dagger 项目的分支. gitHub:https://github.com/google/dagger Dagger2采用依赖注入方式,依赖注入是一种面向对象的编程模式,它的出现是为

深入解析Linux内核及其相关架构的依赖关系

Linux kernel 成功的两个原因: 灵活的架构设计使得大量的志愿开发者能够很容易加入到开发过程中:每个子系统(尤其是那些需要改进的)都具备良好的可扩展性.正是这两个原因使得Linux kernel可以不断进化和改进. 一.Linux内核在整个计算机系统中的位置 分层结构的原则: the dependencies between subsystems are from the top down: layers pictured near the top depend on lower la