web-dev-qa-db-fra.com

Pourquoi ai-je le message d'erreur "Le type de données XML n'est pas pris en charge dans les requêtes distribuées" lorsque je demande à un serveur lié des données non xml?

J'ai deux serveurs SQL (exécutant SQL Server 2008) nommés DATA01 et DATA02. DATA02 a une définition de serveur lié LINK, qui pointe sur DATA01, avec le mappage utilisateur approprié configuré. Sur DATA01 il y a une base de données MyDatabase contenant ces deux tables:

CREATE TABLE T_A (
    Id int
)

CREATE TABLE T_B (
    Id int,
    Stuff xml
)

Lorsque j'exécute cette commande à partir de DATA02, les données sont renvoyées comme prévu:

SELECT Id FROM LINK.MyDatabase.dbo.T_A;

Cependant, lorsque j'exécute cette commande à partir de DATA02, j'obtiens une erreur:

SELECT Id, Stuff FROM LINK.MyDatabase.dbo.T_B;

L'erreur est

Le type de données XML n'est pas pris en charge dans les requêtes distribuées. L'objet distant 'DATA02.MyDatabase.dbo.T_B' possède une ou plusieurs colonnes xml.

Et étrangement, cette commande:

SELECT Id FROM LINK.MyDatabase.dbo.T_B;

donne également la même erreur, même si je ne suis pas SELECTing la colonne xml! Que se passe-t-il?

49
AakashM

Ceci est une lacune dans SQL Server. La simple existence d'une colonne xml sur la table l'empêche de participer aux requêtes distribuées (par exemple, être interrogé via une connexion de serveur lié). Ceci est mentionné dans la documentation , mais pas de manière particulièrement visible. Vous pouvez voir le rapport de bug principal ici , et un rapport similaire ici . Ce dernier donne deux solutions de contournement:

  1. Créez une vue [sans] colonne (s) XML sur le serveur distant et interrogez-la.

    Dans votre exemple, cela impliquerait d'ajouter une vue à MyDatabase qui ressemble à ceci:

    CREATE VIEW V_T_B AS SELECT Id FROM T_B;
    

    Vous pouvez ensuite interroger cette vue via le lien pour obtenir les données Id. Notez que quelque chose comme

    SELECT Id FROM ( SELECT Id FROM T_B ) T_B;
    

    ne fonctionne pas .

  2. Utilisez une requête directe dans le formulaire

    SELECT * from OPENQUERY (... )
    

    Cette méthode présente l'avantage de ne nécessiter aucune modification de la base de données source; L'inconvénient est qu'il n'est plus possible d'utiliser le nommage standard en quatre parties pour les données locales et liées. La requête ressemblerait à

    SELECT Id FROM OPENQUERY(DATA02, 'SELECT Id FROM T_B') T_B;
    

    Notez que si voulez voulez les données xml, cette méthode (ainsi que la conversion vers et depuis un type de données non xml) sera requise :

    SELECT Id, CAST(Stuff AS XML) Stuff 
    FROM OPENQUERY(DATA02, 'SELECT Id, CAST(Stuff AS nvarchar(max)) Stuff 
                            FROM T_B') T_B;
    

Notez que le bogue a été signalé pour la première fois dans SQL Server 2005 et qu'il n'est pas corrigé dans SQL Server 2014.

87
AakashM

Essaye ça:

  • Créez une vue du côté source avec la conversion XML en nvarchar (max):

CREATE VIEW vXMLTestASSELECT cast (Stuff as nvarchar (max)) as STUFF FROM T_B

  • Vous pouvez le sélectionner du côté de la destination avec la conversion en XML.

SELECT Cast (Stuff as XML) en tant que Stuff FROM OPENQUERY (DATA02, 'SELECT Stuff FROM vXMLTest')

Cette solution fonctionne pour moi en 2008R2.

9
Csaba Molnár

J'ai trouvé un autre moyen de le faire:

  1. Créez un Linked Server sur DATA01, DATA02 ou même un troisième serveur (qui pourrait être local).
  2. Exécutez votre requête en utilisant EXEC [linked_server].[sp_sqlexecute].

Pourquoi je préfère cette méthode à la création de vues ou à l'utilisation de OPENQUERY?

  1. Cette méthode ne nécessite aucune autorisation sur le serveur lié (vous n'avez pas besoin de créer de vues).
  2. Vous pouvez passer un paramètre à votre requête en utilisant la syntaxe sp_sqlexecute ( pour plus d'informations sur sp_sqlexecute ici ). Pour OPENQUERY, vous devez passer STRING ou TEXT_Lex, ce qui signifie que toutes les valeurs doivent être saisies directement dans cette fonction.

Voici l'exemple:

DECLARE @UserID UNIQUEIDENTIFIER = ''

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

EXEC [linked_server].[dbo].[sp_executesql] 
    @SearchForUserQuery,
    @SearchForUserParams,
    @UserID = @UserID
1
semptra