升级到 SQL Server 2022 后链接服务器上的更新失败

Ser*_*kin 5 sql-server linked-server update sql-server-2022

将 SQL Server 从 2019 年升级到 2022 后,以下使用链接服务器的查询开始失败(为了简单起见,我省略了一些列名称和详细信息):

UPDATE [<remote server>].[<remote db>].dbo.Projects
SET    RemoteColumnName = p.LocalColumnName
FROM   prod.Projects p
WHERE  p.ProjectID = Projects.ProjectID;
Run Code Online (Sandbox Code Playgroud)

然而,不涉及本地表工作的基本更新就很好了。SQL Server 报告以下错误,我什至在文档中找不到该错误:

UPDATE [<remote server>].[<remote db>].dbo.Projects
SET    RemoteColumnName = p.LocalColumnName
FROM   prod.Projects p
WHERE  p.ProjectID = Projects.ProjectID;
Run Code Online (Sandbox Code Playgroud)

两台服务器都是 SQL Server 2022,在升级之前一切正常。

为本地和远程表添加 DDL,以防万一。但是,问题似乎与模式无关,并且在具有不同数据类型的不同表上重现。

-- Table on the remote linked server
CREATE TABLE dbo.Projects
(
    ProjectID               BIGINT           NOT NULL,
    RemoteColumnName        VARCHAR(255)     NOT NULL,
    CONSTRAINT PK_Projects PRIMARY KEY CLUSTERED (ProjectID)
)

-- Table on the local server
CREATE TABLE prod.Projects
(
    ProjectID               BIGINT           NOT NULL,
    LocalColumnName         VARCHAR(255)     NOT NULL,
    CONSTRAINT PK_Projects PRIMARY KEY CLUSTERED (ProjectID)
)
Run Code Online (Sandbox Code Playgroud)

添加链接服务器定义脚本。帐户RemoteUser具有远程服务器上目标数据库的 db_owner 角色。两个数据库的兼容级别均为 160。即使在空表上也会重现该问题。

EXEC master.dbo.sp_addlinkedserver @server = N'IS', @srvproduct=N'SQL Server'
EXEC master.dbo.sp_addlinkedsrvlogin @rmtsrvname=N'IS',@useself=N'False',@locallogin=NULL,@rmtuser=N'RemoteUser',@rmtpassword='########'
GO
EXEC master.dbo.sp_serveroption @server=N'IS', @optname=N'collation compatible', @optvalue=N'false'
EXEC master.dbo.sp_serveroption @server=N'IS', @optname=N'data access', @optvalue=N'true'
EXEC master.dbo.sp_serveroption @server=N'IS', @optname=N'dist', @optvalue=N'false'
EXEC master.dbo.sp_serveroption @server=N'IS', @optname=N'pub', @optvalue=N'false'
EXEC master.dbo.sp_serveroption @server=N'IS', @optname=N'rpc', @optvalue=N'true'
EXEC master.dbo.sp_serveroption @server=N'IS', @optname=N'rpc out', @optvalue=N'true'
EXEC master.dbo.sp_serveroption @server=N'IS', @optname=N'sub', @optvalue=N'false'
EXEC master.dbo.sp_serveroption @server=N'IS', @optname=N'connect timeout', @optvalue=N'0'
EXEC master.dbo.sp_serveroption @server=N'IS', @optname=N'collation name', @optvalue=NULL
EXEC master.dbo.sp_serveroption @server=N'IS', @optname=N'lazy schema validation', @optvalue=N'false'
EXEC master.dbo.sp_serveroption @server=N'IS', @optname=N'query timeout', @optvalue=N'0'
EXEC master.dbo.sp_serveroption @server=N'IS', @optname=N'use remote collation', @optvalue=N'true'
EXEC master.dbo.sp_serveroption @server=N'IS', @optname=N'remote proc transaction promotion', @optvalue=N'false'
Run Code Online (Sandbox Code Playgroud)

Pau*_*ite 5

如果您对调用数据库中的任何表(而不仅仅是您在查询中引用的表)使用动态数据屏蔽,则会出现此错误。

在调用数据库中曾经使用过 DDM就足够了。一旦使用,即使删除使用 DDM 的每个表,错误仍然存​​在。我还没有找到重置此状态的方法。

您可以使用该文档链接中的查询来检查使用 DDM 的表:

SELECT c.name, tbl.name as table_name, c.is_masked, c.masking_function  
FROM sys.masked_columns AS c  
JOIN sys.tables AS tbl   
    ON c.[object_id] = tbl.[object_id]  
WHERE is_masked = 1;
Run Code Online (Sandbox Code Playgroud)

此错误是意外行为,您应该向 Microsoft 支持报告,以便他们调查修复。

在修复可用之前,可以采取多种解决方法,包括按照您的建议用删除和插入序列替换更新。

另一种选择是将匹配的源行复制到临时表或表变量,并在更新中引用它,而不是直接引用源表:

SELECT P.* 
INTO #Projects
FROM prod.Projects AS P
WHERE EXISTS
(
    SELECT * 
    FROM [<remote server>].[<remote db>].dbo.Projects AS RP
    WHERE RP.ProjectID = P.ProjectID
);

UPDATE RP
SET RP.RemoteColumnName = P.LocalColumnName
FROM #Projects AS P
JOIN [<remote server>].[<remote db>].dbo.Projects AS RP
    ON RP.ProjectID = P.ProjectID;
Run Code Online (Sandbox Code Playgroud)

对于那些感兴趣的人,编译期间第二次调用ExtractSourceNames时会发生错误:

调用栈

  • 感谢保罗如此深入的分析,确实,我在另一个表的调用数据库中有一个数据屏蔽。希望这个问题可以在即将推出的 CU 中得到解决,目前我们将使用 DELETE/INSERT 解决方法。 (2认同)