了解 SQL 依赖关系

SQL 依赖关系是在 SQL 表达式中使用的按名称引用,可使一个实体依赖于另一个实体。在其定义中引用了另一个实体并且该定义存储在系统目录中的实体称为“引用实体”。被另一个实体引用的实体称为“被引用的实体”。数据库引擎跟踪两种类型的依赖关系。

  • 绑定到架构的依赖关系

    绑定到架构的依赖关系是一种两个实体之间的关系,只要引用实体存在,这种关系就可以防止被引用的实体被删除或更改。绑定到架构的依赖关系是在使用 WITH SCHEMABINDING 子句创建视图或用户定义函数时创建的。绑定到架构的依赖关系也可以于表在 CHECK 或 DEFAULT 约束或计算列定义中引用另一个实体(例如 Transact-SQL 用户定义函数、用户定义类型或 XML 架构集合)时创建。使用由两部分 (schema_name.object_name) 组成的名称指定对象不能称为绑定到架构的引用。

  • 非绑定到架构的依赖关系

    非绑定到架构的依赖关系也是一种两个实体之间的关系,但这种关系并不防止被引用的实体被删除或修改。

下图阐释了 SQL 依赖关系的示例。

SQL 依赖关系说明

图中包含两个实体:过程 X 和过程 Y。过程 X 包含一个对过程 Y 按名称引用的 SQL 表达式。过程 X 称为“引用实体”,而过程 Y 称为“被引用的实体”。由于过程 X 依赖于过程 Y,因此当过程 Y 不存在时,过程 X 将失败,并出现运行时错误。但是,当过程 X 不存在时,过程 Y 也不会失败。

下面的示例说明存储过程 X 如何依赖于存储过程 Y。

USE tempdb;
GO
CREATE PROCEDURE dbo.Y AS
SELECT * FROM sys.objects
GO
CREATE PROCEDURE dbo.X as
    EXEC dbo.Y;
GO

若要查看 X 对 Y 的依赖关系,请运行以下查询。

SELECT * 
FROM sys.sql_expression_dependencies 
WHERE referencing_id = OBJECT_ID('X')
    AND referenced_id = OBJECT_ID('Y')
    AND referenced_schema_name = 'dbo'
    AND referenced_entity_name = 'Y'
    AND referenced_database_name IS NULL
    AND referenced_server_name IS NULL;
GO

引用实体和被引用的实体的类型

下表列出了为其创建和维护依赖关系信息的实体类型。此表指示了将相应实体作为引用实体还是被引用的实体进行跟踪。不为规则、默认值、临时表、临时存储过程或系统对象创建或维护依赖关系信息。

实体类型

引用实体

被引用的实体

是*

视图

Transact-SQL 存储过程**

CLR 存储过程

Transact-SQL 用户定义函数

CLR 用户定义函数

CLR 触发器(DML 和 DDL)

Transact-SQL DML 触发器

Transact-SQL 数据库级 DDL 触发器

Transact-SQL 服务器级 DDL 触发器

扩展存储过程

队列

同义词

类型(别名和 CLR 用户定义类型)

XML 架构集合

分区函数

* 仅当表在计算列定义、CHECK 约束或 DEFAULT 约束中引用 Transact-SQL 模块、用户定义类型或 XML 架构集合时,才会将该表作为引用实体进行跟踪。

** 整数值大于 1 的带编号的存储过程将不会作为引用实体或被引用的实体进行跟踪。

如何跟踪依赖关系信息

数据库引擎将在创建、更改或删除引用实体时自动跟踪依赖关系信息,并将此信息记录在 SQL Server 系统目录中。例如,如果您创建了一个引用表的触发器,则这些实体间的依赖关系将被记录下来。如果您随后删除了该触发器,依赖关系信息将从系统目录中删除。

SQL Server 早期版本中的依赖关系按 ID 跟踪,与之不同的是,当前版本中的依赖关系按名称跟踪。也就是说,即使引用实体创建时被引用的实体并不存在,数据库引擎仍可跟踪两个实体间的依赖关系信息。之所以会发生这种情况,是因为存在延迟名称解析。例如,即使数据库中并不存在存储过程所引用的表,也可以成功创建该存储过程,但是该存储过程不能执行。数据库引擎记录此过程和表之间的依赖关系,但是,因为对象尚未存在,所以无法记录表 ID。如果随后创建了该表,则表 ID 将随其他依赖关系信息返回。

当被引用的实体按名称显示在引用实体的持久化 SQL 表达式中时,依赖关系信息将会被跟踪。采用以下方式按名称引用实体时,将会获得依赖关系信息:

  • 通过在 Transact-SQL模块定义中使用下列任一语句:

    • 数据操作语言 (DML) 语句(SELECT、INSERT、UPDATE、DELETE 或 MERGE)

    • EXECUTE

    • DECLARE

    • SET(当 SET 与用户定义函数或用户定义类型一起使用时。例如:DECLARE @var int; SET @var = dbo.udf1。)

    不会跟踪使用数据定义语言 (DDL) 语句(如 CREATE、ALTER 或 DROP)在 Transact-SQL 模块定义中引用的实体。

  • 通过使用 CREATE、ALTER 或 DROP TABLE 语句,条件是这些语句不在 Transact-SQL 模块中并且被引用的实体为在计算列、CHECK 约束或 DEFAULT 约束中定义的 Transact-SQL 用户定义函数、用户定义类型或 XML 架构集合时。

跨数据库和跨服务器的依赖关系

当某个实体通过使用由三部分组成的有效名称引用另一个实体时,将创建跨数据库的依赖关系。当某个实体通过使用由四部分组成的有效名称引用另一个实体时,将创建跨服务器的引用。仅当显式指定服务器和数据库的名称时,才会记录相应的名称。例如,当指定为 MyServer.MyDB.MySchema.MyTable 时,会记录服务器和数据库的名称,但是,当指定为 MyServer..MySchema.MyTable 时,将只记录服务器的名称。有关有效的多部分名称的详细信息,请参阅 Transact-SQL 语法约定 (Transact-SQL)

存在下列限制:

  • 不跟踪 OPENROWSET、OPENQUERY 和 OPENDATASOURCE 语句的跨服务器依赖关系。

  • 不跟踪语句 EXEC (' ') AT linked_server 的依赖关系。

下表总结了被跟踪的跨服务器和跨数据库依赖关系以及会在系统目录中记录并由 sys.sql_expression_dependencies (Transact-SQL) 报告的信息。

模块中的 SQL 表达式

是否被跟踪

被引用的服务器名称

被引用的数据库名称

被引用的架构名称

被引用的实体名称

SELECT * FROM s1.db2.sales.t1

s1

db2

sales

t1

SELECT * FROM db3..t1

 

db3

 

t1

EXEC db2.dbo.Proc1

 

db2

dbo

proc1

EXEC (' ') AT linked_srv1

 

 

 

 

EXEC linked_svr1.db2.sales.proc2

linked_svr1

db2

sales

proc2

排序规则对依赖关系跟踪的影响

排序规则确定数据的排序和比较规则。数据库的排序规则将用来标识数据库中各实体的依赖关系信息。例如,如果某个存储过程引用了使用区分大小写排序规则的数据库中的实体 Some_Table 和 SOME_TABLE,则这两个实体的依赖关系信息会被记录下来,因为通过比较这两个名称表明它们并不一样。但是,如果数据库使用不区分大小写的排序规则,则仅会记录一个依赖关系。

对于跨服务器和跨数据库的依赖关系,引用对象所在服务器的排序规则将用于解析服务器和数据库的名称。当前数据库的排序规则将用于解析架构名称和对象名称。

请看下面的存储过程定义。如果存储过程是在使用区分大小写的排序规则的数据库中对使用不区分大小写的服务器排序规则的 SQL Server 实例创建的,则将记录实体 srv_referenced.db_referenced.dbo.p_referenced 和 srv_referenced.db_referenced.DBO.P_REFERENCED 的两种依赖关系。

CREATE PROCEDURE p_referencing AS
    EXECUTE srv_referenced.db_referenced.dbo.p_referenced
    EXECUTE srv_referenced.db_referenced.DBO.P_REFERENCED
    EXECUTE SRV_REFERENCED.DB_REFERENCED.dbo.p_referenced;

解析不明确的引用

当某个引用可以在运行时解析为用户定义函数、用户定义类型 (UDT) 或 xml 类型的列的 Xquery 引用时,即可称为不明确的引用。

请看下面的存储过程定义。

CREATE PROCEDURE dbo.p1 AS 
    SELECT column_a, Sales.GetOrder() FROM Sales.MySales; 

创建此存储过程时,尚不明确 Sales.GetOrder() 是对 Sales 架构中名为 GetOrder 的用户定义函数的引用还是通过使用名为 GetOrder() 的方法对一个名为 Sales 的 UDT 类型的列的引用。当引用不明确时,通过将 sys.sql_expression_dependencies 和 sys.dm_sql_referenced_entities 中的 is_ambiguous 列设置为 1 来报告依赖关系不明确。报告以下依赖关系信息:

  • 存储过程和表之间的依赖关系。

  • 存储过程和用户定义函数之间的依赖关系。如果函数存在,将报告函数的 ID;否则,ID 将为 NULL。

  • 函数的依赖关系将标记为不明确的依赖关系。也就是说,is_ambiguous 将设为 1。

  • 不会报告列级依赖关系,因为无法绑定引用列的语句。

维护依赖关系

数据库引擎对绑定到架构和非绑定到架构的依赖关系都进行维护。这些依赖关系会在发生了任何影响依赖关系跟踪的操作时自动刷新,例如当从 SQL Server 的早期版本升级数据库或更改数据库的排序规则时。