在查询链接服务器中是否存在非xml数据时,为什么会出现"分布式查询中不支持Xml数据类型"的错误?

Aak*_*shM 57 xml sql-server linked-server

我有两个SQL服务器(运行SQL Server 2008)命名DATA01DATA02.DATA02具有链接的服务器定义LINK,指向DATA01适当的用户映射设置.在DATA01那里有一个MyDatabase包含这两个表的数据库:

CREATE TABLE T_A (
    Id int
)

CREATE TABLE T_B (
    Id int,
    Stuff xml
)
Run Code Online (Sandbox Code Playgroud)

当我从中运行此命令时DATA02,我按预期返回数据:

SELECT Id FROM LINK.MyDatabase.dbo.T_A;
Run Code Online (Sandbox Code Playgroud)

但是,当我运行此命令时DATA02,我收到一个错误:

SELECT Id, Stuff FROM LINK.MyDatabase.dbo.T_B;
Run Code Online (Sandbox Code Playgroud)

错误是

分布式查询不支持Xml数据类型.远程对象'DATA02.MyDatabase.dbo.T_B'具有xml列.

奇怪的是,这个命令:

SELECT Id FROM LINK.MyDatabase.dbo.T_B;
Run Code Online (Sandbox Code Playgroud)

即使我不是SELECTxml列,也会出现同样的错误!这是怎么回事?

Aak*_*shM 92

这是SQL Server中的一个缺陷.表上仅存在 xml列会阻止它参与分布式查询(例如,通过链接服务器连接查询).这在文档中提到,但并不是特别突出.您可以在此处查看主要的Connect错误报告,以及此处类似报告.后者提供了两种解决方法:

  1. 在远程服务器上创建没有XML列的[a]视图并查询它.

    在您的示例中,这将涉及添加一个视图MyDatabase ,如下所示:

    CREATE VIEW V_T_B AS SELECT Id FROM T_B;
    
    Run Code Online (Sandbox Code Playgroud)

    然后,您可以通过链接查询此视图以获取Id 数据.请注意类似的东西

    SELECT Id FROM ( SELECT Id FROM T_B ) T_B;
    
    Run Code Online (Sandbox Code Playgroud)

    不起作用.

  2. 在表单中使用传递查询

    SELECT * from OPENQUERY (... )
    
    Run Code Online (Sandbox Code Playgroud)

    该方法的优点是不需要对源数据库进行任何更改; 缺点是不再可能为本地和链接数据使用标准的四部分命名.查询看起来像

    SELECT Id FROM OPENQUERY(DATA02, 'SELECT Id FROM T_B') T_B;
    
    Run Code Online (Sandbox Code Playgroud)

    请注意,如果你真的这样做需要的XML数据,这种方法(用铸造和从非XML数据类型一起)将需要:

    SELECT Id, CAST(Stuff AS XML) Stuff 
    FROM OPENQUERY(DATA02, 'SELECT Id, CAST(Stuff AS nvarchar(max)) Stuff 
                            FROM T_B') T_B;
    
    Run Code Online (Sandbox Code Playgroud)

请注意,该错误首先在SQL Server 2005中报告,并在SQL Server 2014中保持不变.

  • 您还可以在DATA02上创建存储过程并远程调用存储过程,而不是尝试远程运行查询. (4认同)
  • 刚刚在 SQL 2019 中偶然发现它(作为连接服务器)。也许这只是一个被误解的功能。 (2认同)

Csa*_*nár 10

试试这个:

  • 使用xml强制转换为nvarchar(max)在源端创建视图:

CREATE VIEW vXMLTest AS SELECT cast(Stuff as nvarchar(max))为STUFF FROM T_B

  • 您可以在目标端选择它并使用强制转换为xml

SELECT Cast(Stuff as XML)作为OPENQUERY的东西(DATA02,'SELECT Stuff FROM vXMLTest')

这个解决方案适用于2008R2.


sem*_*tra 7

我找到了另一种方法:

  1. 创建一个Linked Serveron DATA01DATA02甚至是第三个服务器(可能是本地的)。
  2. 使用 执行您的查询EXEC [linked_server].[sp_executesql]

为什么我更喜欢这种方法而不是创建视图或使用OPENQUERY

  1. 此方法不需要对链接服务器具有任何权限(您不需要创建视图)。
  2. 您可以使用sp_executesql语法将参数传递给您的查询(在此处找到有关 sp_sqlexecute 的更多信息)。因为OPENQUERY你必须传递一个STRINGor TEXT_LEX,巫婆意味着所有的值都必须直接输入到这个函数中。

这是示例:

DECLARE @UserID UNIQUEIDENTIFIER = ''

DECLARE @SearchForUserParams NVARCHAR(MAX) = N'@UserID UNIQUEIDENTIFIER';  
DECLARE @SearchForUserQuery NVARCHAR(MAX) = 
N'SELECT
    UserID,
    Username,
    Email,
    CONVERT(NVARCHAR(MAX), UserDataXml) AS UserDataXml
FROM User
WHERE UserID = @UserID'

EXEC [linked_server].[dbo].[sp_executesql] 
    @SearchForUserQuery,
    @SearchForUserParams,
    @UserID = @UserID
Run Code Online (Sandbox Code Playgroud)

  • 当您需要来自生产服务器的一些数据并且无法创建视图时,这是一个很好的答案。但在您的示例中,您忘记了数据库:[linked_server].[DATABASE].dbo.[sp_executesql] ... (3认同)