J'interroge les données d'un serveur lié via une vue sur le serveur d'origine. La vue doit inclure quelques colonnes standardisées, telles queCreated
, Modified
et Deleted
, mais dans ce cas, la table sur le serveur source n'a pas d'informations appropriées. Les colonnes sont donc explicitement converties en leurs types respectifs. J'ai mis à jour la vue, en changeant une colonne de
NULL AS Modified
à
CAST(NULL as DateTime) as Modified
Cependant, après avoir effectué cette mise à jour, la vue déclenche le message d'erreur suivant:
Msg 7341, niveau 16, état 2, ligne 3 Impossible d'obtenir la valeur de ligne actuelle de la colonne "(expression générée par l'utilisateur) .Expr1002" à partir du fournisseur OLE DB "SQLNCLI11" pour le serveur lié "".
Nous avons effectué ce changement de "distribution explicite" généralement sur le serveur d'origine sans soucis, et je soupçonne que le problème pourrait être lié à la version des serveurs impliqués. On n'a pas vraiment besoin d'appliquer ce plâtre, mais il semble plus propre. En ce moment, je suis simplement curieux de savoir pourquoi cela se produit.
Version du serveur (origine):
Microsoft SQL Server 2012 - 11.0.5058.0 (X64) 14 mai 2014 18:34:29 Copyright (c) Microsoft Corporation Enterprise Edition (64 bits) sur Windows NT 6.1 (Build 7601: Service Pack 1) (Hyperviseur)
Version du serveur (lié):
Microsoft SQL Server 2008 R2 (SP1) - 10.50.2500.0 (X64) 17 juin 2011 00:54:03 Copyright (c) Microsoft Corporation Enterprise Edition (64 bits) sur Windows NT 6.1 (Build 7601: Service Pack 1) (Hyperviseur )
modifier
Je viens de réaliser que j'ai fait une erreur en ne publiant pas toutes les colonnes en question, et je dois m'excuser d'avoir omis un détail important. Je ne sais pas comment je n'ai pas remarqué cela plus tôt. La question demeure cependant.
La conversion erronée ne se produit pas avec la conversion en DateTime, mais avec une colonne en cours de conversion en UniqueIdentifier.
C'est le coupable:
CAST(NULL AS UniqueIdentifier) AS [GUID]
Les identificateurs uniques sont pris en charge sur SQL Server 2008 R2 et, comme mentionné dans les commentaires, la requête effectuée par la vue s'exécute correctement sur le serveur lié.
Danish_Norwegian_CI_AS
et le serveur lié a le classementSQL_Danish_Pref_CP1_CI_AS
, mais laCOLLATE
clause ne peut pas être appliquée auxDateTime
colonnes, donc je ne suis pas allé beaucoup plus loin!select Null from ...
requête WITH ou imbriquée etCAST
dans une autre?INT
si vous avez modifié le type de données en le faisant. Je ne sais pas pourquoi cela vous donnerait ce message d'erreur.Réponses:
Ainsi, j'ai pu reproduire l'erreur après avoir réalisé que cela
CAST
se faisait localement, pas sur l'instance distante. J'avais précédemment recommandé de passer au SP3 dans l'espoir de résoudre ce problème (en partie parce que je ne pouvais pas reproduire l'erreur sur SP3, et en partie parce que c'était une bonne idée malgré tout). Cependant, maintenant que je peux reproduire l'erreur, il est clair que passer au SP3, bien que probablement une bonne idée, ne va pas résoudre ce problème. Et j'ai également reproduit l'erreur dans SQL Server 2008 R2 RTM et 2014 SP1 (en utilisant un serveur lié local "loop-back" dans les trois cas).Il semble que ce problème soit lié à emplacement d' exécution de la requête, ou au moins à l'exécution de certaines parties . Je dis cela parce que j'ai réussi à faire
CAST
fonctionner l' opération, mais uniquement en incluant une référence à un objet DB local:Cela fonctionne réellement. Mais ce qui suit obtient l'erreur d'origine:
Je suppose que lorsqu'il n'y a pas de références locales, l'intégralité de la requête est expédiée vers le système distant pour être exécutée, et pour une raison quelconque,
NULL
il ne peut pas être converti enUNIQUEIDENTIFIER
, ou peut-être que leNULL
pilote OLE DB n'est pas traduit correctement.Sur la base des tests que j'ai effectués, cela semble être un bogue, mais je ne sais pas si le bogue se trouve dans SQL Server ou le pilote SQL Server Native Client / OLEDB. Cependant, l'erreur de conversion se produit dans le pilote OLEDB et n'est donc pas nécessairement un problème de conversion à partir de
INT
versUNIQUEIDENTIFIER
(une conversion qui n'est pas autorisée dans SQL Server) car le pilote n'utilise pas SQL Server pour effectuer des conversions (SQL Server ne fait pas non plus permettre la conversionINT
enDATE
, mais le pilote OLEDB gère cela avec succès, comme indiqué dans l'un des tests).J'ai effectué trois tests. Pour les deux qui ont réussi, j'ai regardé les plans d'exécution XML qui montrent la requête qui est exécutée à distance. Pour les trois, j'ai capturé toutes les exceptions ou événements OLEDB via SQL Profiler:
Événements:
Filtres de colonne:
LES TESTS
Test 1
CAST(NULL AS UNIQUEIDENTIFIER)
ça marchePartie pertinente du plan d'exécution XML:
Test 2
CAST(NULL AS UNIQUEIDENTIFIER)
qui échoue(Remarque: j'ai conservé la sous-requête là-dedans, commenté, de sorte que ce serait une différence de moins lorsque je comparais les fichiers de trace XML)
Test 3
CAST(NULL AS DATE)
ça marche(Remarque: j'ai conservé la sous-requête là-dedans, commenté, de sorte que ce serait une différence de moins lorsque je comparais les fichiers de trace XML)
Partie pertinente du plan d'exécution XML:
Si vous regardez le test n ° 3, il fait un
SELECT TOP (2) NULL
sur le système "distant". La trace du Générateur de profils SQL montre que le type de données de ce champ distant est en faitINT
. La trace montre également que le champ côté client (c'est-à-dire où j'exécute la requête) estDATE
, comme prévu. La conversion deINT
àDATE
, quelque chose qui obtiendra une erreur dans SQL Server, fonctionne très bien dans le pilote OLEDB. La valeur distante estNULL
, elle est donc retournée directement, d'où<ColumnReference Column="Expr1002" />
.Si vous regardez le test n ° 1, il fait un
SELECT 1
sur le système "distant". La trace du Générateur de profils SQL montre que le type de données de ce champ distant est en faitINT
. La trace montre également que le champ côté client (c'est-à-dire où j'exécute la requête) estGUID
, comme prévu. La conversion deINT
àGUID
(rappelez-vous, cela se fait dans le pilote, et OLEDB l'appelle "GUID"), quelque chose qui obtiendra une erreur dans SQL Server, fonctionne très bien dans le pilote OLEDB. La valeur distante ne l'est pasNULL
, elle est donc remplacée par un littéralNULL
, d'où<Const ConstValue="NULL" />
.Le test n ° 2 échoue, il n'y a donc pas de plan d'exécution. Cependant, il interroge le système "distant" avec succès, mais ne peut tout simplement pas renvoyer l'ensemble de résultats. La requête capturée par SQL Profiler est:
C'est exactement la même requête qui est effectuée dans le test n ° 1, mais ici, elle échoue. Il existe d'autres différences mineures, mais je ne peux pas interpréter complètement la communication OLEDB. Cependant, le champ distant s'affiche toujours sous la forme
INT
(wType = 3 = adInteger / entier signé sur quatre octets / DBTYPE_I4) tandis que le champ "client" s'affiche toujours sous la formeGUID
(wType = 72 = adGUID / identificateur global unique / DBTYPE_GUID). La documentation OLE DB ne nous aide pas beaucoup comme GUID Type de données Conversions , DBDATE Type de données Conversions et I4 des types de données Conversions montrent que la conversion de I4 soit GUID ou DBDATE est non pris en charge, mais lesDATE
travaux de la requête.Les fichiers XML de trace pour les trois tests se trouvent sur PasteBin. Si vous voulez voir les détails de la différence entre chaque test et les autres, vous pouvez les enregistrer localement puis faire un "diff" sur eux. Les fichiers sont:
ERGO?
Que faire à ce sujet? Probablement juste la solution de contournement que j'ai notée dans la section supérieure, étant donné que SQL Native Client -
SQLNCLI11
- est obsolète à partir de SQL Server 2012. La plupart des pages MSDN sur le sujet de SQL Server Native Client ont l'avis suivant sur le Haut:Pour plus d'informations, veuillez consulter:
ODBC ??
J'ai configuré un serveur lié ODBC via:
Et puis essayé:
et a reçu l'erreur suivante:
PS
En ce qui concerne le transport des GUID entre les serveurs distants et locaux, les valeurs non NULL sont gérées via une syntaxe spéciale. J'ai remarqué les informations d'événement OLE DB suivantes dans la trace du Générateur de profils SQL lorsque j'ai exécuté
CAST(0x00 AS UNIQUEIDENTIFIER)
:PPS
J'ai également testé via
OPENQUERY
avec la requête suivante:et il a réussi, même sans la référence d'objet local. Le fichier XML de trace de SQL Profiler a été publié dans PasteBin à l'adresse suivante:
NullGuidSuccessOPENQUERY.xml
Le plan d'exécution XML le montre en utilisant une
NULL
constante, comme dans le test n ° 1.la source
Il n'y a qu'une solution de contournement laide - utilisez une constante de date comme
'1900-01-01'
au lieu denull
.Après l'importation, vous pouvez mettre à jour les colonnes avec
1900-01-01
retour à Null.C'est une sorte de fonctionnalité / bogue de SQL 2012 comme ici .
Modifier: remplacé
1900-00-00
par une date valide1900-01-01
selon le commentaire @a_horse_with_no_name ci-dessous.la source
uniqueidentifier
colonne. Ou peut-être pourrait-on l'adapter - quelque chose commeCAST('00000000-0000-0000-0000-000000000000' AS UniqueIdentifier) AS [GUID]
, peut-être?1900-00-00
est une date invalide et ne sera pas acceptée.Le problème est lié aux conversions de types de données (comme indiqué dans les commentaires).
Considérer ce qui suit:
Notez que le
NullColumn
est de typeint
. SQL Server n'aime pas convertir lesint
valeurs enuniqueidentifier
. CetteSELECT
instruction échouera lors d'une conversion de type de données:Bien que cette valeur spécifique (NULL) puisse être convertie en GUID, SQL Server renvoie l'erreur en fonction de la conversion du type de données, avant même de regarder les valeurs spécifiques. Au lieu de cela, vous devrez effectuer une
CAST
opération en plusieurs étapes pour modifier l'impliciteint
en un type de données qui peut être converti proprement enuniqueidentifer
- ce qui signifie couler d'abord pourvarchar
ensuiteuniqueidentifier
:la source
Le PO peut finalement décider s'il s'agit d'une réponse appropriée.
Je n'ai pas de preuve `` absolue '', mais je `` soupçonne '' que le problème provient du fait qu'un UniqueIdentifer dépend du serveur et peut-être que le fournisseur a du mal à déterminer de quel serveur (local ou distant) il peut obtenir cet identifiant unique, même s'il est nul. C'est pourquoi vous pouvez probablement convertir n'importe quel autre type de données avec succès dans ce scénario, mais pas l'identificateur unique. Les types de données qui dépendent du «serveur» comme UNIQUEIDENTIFIERS et DATETIMEOFFSET vous donneront l'erreur que vous rencontrez.
L'utilisation d'OPENQUERY au lieu d'un nom en 4 parties fonctionne.
la source
Uniqueidentifier
etDateTimeOffset
dépendant du serveur? Avez-vous des sources pour cela?Solution: la réponse acceptée semble indiquer que la conversion doit avoir lieu localement car le pilote OLEDB ne la prend pas en charge.
Je pense donc qu'une solution de contournement simple (au moins dans le cas de ma requête qui sélectionne un null
uniqueidentifier
dans le cas de base d'un CTE récursif) consiste à déclarer une variable nulle:la source