排除 SQL 依赖关系的故障

本主题介绍常见对象依赖关系问题及其解决方案。

动态管理函数 sys.dm_sql_referenced_entities 不返回列级依赖关系

sys.dm_sql_referenced_entities 系统函数将报告绑定到架构的引用的任何列级依赖关系。例如,此函数将报告索引视图的所有列级依赖关系,因为索引视图需要架构绑定。但是,如果被引用实体不绑定到架构,则仅当引用列的所有语句可以绑定时,才会报告列依赖关系。只有在分析语句时所有对象都存在,才可以成功绑定语句。如果在实体中定义的任何语句无法绑定,将不报告列依赖关系,并且 referenced_minor_id 列将返回 0。无法解析列依赖关系时,将引发错误 2020。此错误不会阻止查询返回对象级别的依赖关系。

解决方案

更正消息中标识的错误 2020 之前的任何错误。例如,在以下代码示例中,视图 Production.ApprovedDocuments 在 Production.Document 表的列Title、ChangeNumber 和 Status 中定义。将向 sys.dm_sql_referenced_entities 系统函数查询 ApprovedDocuments 视图所依赖的对象和列。因为视图不是使用 WITH SCHEMA_BINDING 子句创建的,所以可以在被引用表中修改视图中引用的列。该示例通过将 Production.Document 表中的列 ChangeNumber 重命名为 TrackingNumber 对其进行更改。将再次向目录视图查询 ApprovedDocuments 视图;但是它不能绑定到该视图中定义的所有列。将返回错误 207 和 2020 来标识问题。若要解决问题,必须改变视图,以反映列的新名称。

USE AdventureWorks;
GO
CREATE VIEW Production.ApprovedDocuments
AS
    SELECT Title, ChangeNumber, Status
    FROM Production.Document
    WHERE Status = 2;
GO
SELECT referenced_schema_name AS schema_name
    ,referenced_entity_name AS table_name
    ,referenced_minor_name AS referenced_column
FROM sys.dm_sql_referenced_entities ('Production.ApprovedDocuments', 'OBJECT');
GO
EXEC sp_rename 'Production.Document.ChangeNumber', 'TrackingNumber', 'COLUMN';
GO
SELECT referenced_schema_name AS schema_name
    ,referenced_entity_name AS table_name
    ,referenced_minor_name AS referenced_column
FROM sys.dm_sql_referenced_entities ('Production.ApprovedDocuments', 'OBJECT');
GO

该查询返回以下错误消息。

消息 207,级别 16,状态 1,过程 ApprovedDocuments,第 3 行

列名 'ChangeNumber' 无效。

消息 2020,级别 16,状态 1,第 1 行

针对实体 "Production.ApprovedDocuments" 报告的依赖关系不包括对列的引用,这是由于实体引用的对象不存在,或者实体中的一条或多条语句有错误。在重新运行查询之前,请确保实体中没有错误,并且实体引用的所有对象均存在。

下面的示例更正视图中的列名。

USE AdventureWorks;
GO
ALTER VIEW Production.ApprovedDocuments
AS
    SELECT Title,TrackingNumber, Status
    FROM Production.Document
    WHERE Status = 2;
GO

列 is_ambiguous 报告与用户定义函数不一致的值

对于用户定义函数,列 is_ambiguous 报告的值可能不一致。目录视图 sys.sql_expression_dependencies 中的 is_ambiguous 列以及动态函数 sys.dm_sql_referenced_entities 指示对实体的引用不明确。也就是,实体可以在运行时解析为用户定义函数、用户定义类型 (UDT) 或对类型 xml 的列的 XQuery 引用。根据用户定义函数的引用方式,实体类型可能是清楚的,也可能是不清楚的,这会导致 is_ambiguous 列在一种情况下设置为 1 (true),在另一种情况下设置为 0 (false)。例如,请考虑以下存储过程。

CREATE PROCEDURE dbo.p1 
AS
    SELECT Sales.GetOrder() FROM t1;
    SELECT Sales.GetOrder();

在第一个 SELECT 语句中,不清楚 Sales.GetOrder() 是 Sales 架构中的用户定义函数,还是具有 GetOrder() 方法且类型为 UDT 的 Sales 列。在这种情况下,被引用实体 Sales.GetOrder() 的 is_ambiguous 列将设置为 1。在第二个 SELECT 语句中,对 Sales.GetOrder() 的引用是清楚的;根据语法,它只能是对用户定义函数的引用。在这种情况下,is_ambiguous 列设置为 0。此行为会使得列 is_ambiguous 中报告的值可能不一致。了解如何确定 is_ambiguous 列的值可以明确报告的值。

在下列情况下,is_ambiguous 列设置为 0 (false):

  • 很明显,引用指向用户定义函数。也就是,查询绑定到用户定义函数,并且具有该名称的列 UDT 方法或类型为 xml 的列不存在。

  • 很明显,引用指向列 UDT 方法。也就是,具有该 UDT 方法的列存在,并且具有该名称的用户定义函数或类型为 xml 的列不存在。

在下列情况下,is_ambiguous 列设置为 1 (true):

  • 具有被引用名称的用户定义函数、列 UDT 方法或类型为 xml 的列不存在。

  • 被引用名称存在于多个实体。例如,用户定义函数和列 UDT 方法具有相同名称。

对于本质上不明确的实体,referenced_database_name 和 referenced_schema_name 列可能无效。例如,请考虑以下用户定义函数:

CREATE FUNCTION GetNextEmpHierarchyId (@empname varchar(25))
RETURNS hierarchyid
AS
BEGIN
    RETURN 
(
    SELECT h.empid.GetDescendant((SELECT MAX(h1.empid)  
                                  FROM dbo.Employees AS h1  
                                  WHERE h1.empid.GetAncestor(1) = h.empid), NULL)
    FROM dbo.Employees AS h  
    WHERE h.empname = @empname  
)  
END;

由于 hierarchyid UDT 方法调用,列 referenced_database_name 和 referenced_schema_name 对于该函数无效。不清楚对 h.empid.GetDescendant 和 h1.empid.GetAncestor 的引用是对使用由三部分构成的名称的实体 (database.schema.object) 的引用,还是对 UDT 方法 (table.column.method) 的引用。

解决方案

用户不需要执行任何操作。

不报告跨数据库依赖关系的列 referenced_id

不会在 sys.sql_expression_dependencies 目录视图中对跨数据库引用解析 referenced_id 列。仅当显式指定数据库名称和架构名称时,才会记录相应的名称。例如,当指定为 MyDB.MySchema.MyTable 时,会记录数据库和架构名称;但是,当指定为 MyDB..MyTable 时,将只记录数据库名称。

仅当被引用实体可以成功绑定时,才会在 sys.dm_sql_referenced_entities 系统函数中为跨数据库引用报告 referenced_id。绑定可能由于几个原因(包括下列原因)而失败:

  • 数据库处于脱机状态。

  • 被引用实体不存在于数据库中。

解决方案

验证数据库是否处于联机状态,并且被引用实体是否存在于数据库中。

数据库内被引用实体的列 referenced_id 为 Null

sys.dm_sql_referenced_entities 系统函数和 sys.sql_expression_dependencies 系统视图将报告任何绑定到架构的被引用实体的 ID。但是,当被引用实体的 ID 不能确定时,数据库内非绑定到架构的引用的 referenced_id 列为 NULL。在以下情况下可能会出现这种情形:

  • 被引用实体不存在于数据库中。

  • 名称解析依赖调用方。在这种情况下,is_caller_dependent 列设置为 1。

解决方案

验证被引用实体是否存在于数据库中。如果未找到,则创建实体;如果实体存在,则确保符合以下要求:

  • 被引用实体的名称拼写正确。

  • 指定的名称符合数据库的排序规则要求。如果数据库使用区分大小写的排序规则,则指定名称的大小写必须与对象名称的大小写完全匹配。例如,如果指定为 saleshistory,将不会在使用区分大小写排序规则的数据库中找到名为 SalesHistory 的对象的 ID。

  • 已指定对象的架构名称。如果对象不在调用方的默认架构、sys 架构或 dbo 架构中,则需要使用由两部分构成的名称 (schema_name.object_name)。

修改引用实体的定义以符合上面的要求。

如果被引用实体依赖调用方,则通过为被引用实体指定由两部分构成的名称,修改引用实体的定义。有关依赖调用方的引用的详细信息,请参阅报告 SQL 依赖关系

master 数据库中对象的依赖关系信息不报告

可以对在 master 数据库中创建的用户定义实体创建和维护 SQL 依赖关系。如果不报告实体的 SQL 依赖关系,则执行下列步骤:

  • 确保实体是有效类型的依赖关系跟踪

    并非跟踪所有用户对象的依赖关系信息。对于为其创建和维护依赖关系信息的实体类型的列表,请参阅了解 SQL 依赖关系

  • 确保实体未标记为系统对象。

    sys.objects 目录视图中查询实体的 is_ms_shipped 列。如果此列设置为 1,则实体为 SQL Server 附带的系统对象,或者为通过手动将此列设置为 1 从而修改为模拟系统对象的用户定义对象。

解决方案

如果不支持对象的类型,依赖关系信息将不可用。

不跟踪系统对象的依赖关系。如果实体是用户定义的,且要让 SQL Server 在实体上创建和维护依赖关系,则 is_ms_shipped 列必须重置为 0。