Daher konnte ich den Fehler reproduzieren, nachdem ich festgestellt hatte, dass CAST
er lokal und nicht auf der Remote-Instanz ausgeführt wurde. Ich hatte zuvor empfohlen, auf SP3 zu wechseln, in der Hoffnung, dies zu beheben (teilweise, weil der Fehler auf SP3 nicht reproduziert werden kann, und teilweise, weil es trotzdem eine gute Idee ist). Jetzt, da ich den Fehler reproduzieren kann, ist es klar, dass das Hochrüsten auf SP3, obwohl es wahrscheinlich immer noch eine gute Idee ist, dies nicht beheben wird. Außerdem habe ich den Fehler in SQL Server 2008 R2 RTM und 2014 SP1 reproduziert (wobei in allen drei Fällen ein lokaler "Loopback" -Verbindungsserver verwendet wurde).
Es scheint, dass dieses Problem damit zusammenhängt, wo die Abfrage ausgeführt wird oder zumindest, wo Teile davon ausgeführt werden. Ich sage dies, weil ich die CAST
Operation zum Laufen bringen konnte, aber nur durch Einfügen eines Verweises auf ein lokales DB-Objekt:
SELECT rmt.*, CAST(NULL AS UNIQUEIDENTIFIER) AS [GUID]
FROM [Local].[database_name].[dbo].[table_name] rmt
CROSS JOIN (SELECT TOP (1) 1 FROM [sys].[data_spaces]) tmp(dummy);
Das funktioniert tatsächlich. Aber das Folgende bekommt den ursprünglichen Fehler:
SELECT rmt.*, CAST(NULL AS UNIQUEIDENTIFIER) AS [GUID]
FROM [Local].[database_name].[dbo].[table_name] rmt
CROSS JOIN (VALUES (1)) tmp(dummy);
Ich vermute, dass, wenn es keine lokalen Verweise gibt, die gesamte Abfrage an das Remotesystem gesendet wird, um ausgeführt zu werden, und aus irgendeinem Grund NULL
nicht konvertiert werden UNIQUEIDENTIFIER
kann oder möglicherweise NULL
vom OLE DB-Treiber falsch übersetzt wird.
Basierend auf den Tests, die ich durchgeführt habe, scheint dies ein Fehler zu sein, aber ich bin nicht sicher, ob der Fehler in SQL Server oder dem SQL Server Native Client / OLEDB-Treiber vorliegt. Der Konvertierungsfehler tritt jedoch im OLEDB-Treiber auf und ist daher nicht unbedingt ein Problem beim Konvertieren von INT
nach UNIQUEIDENTIFIER
(eine Konvertierung, die in SQL Server nicht zulässig ist), da der Treiber SQL Server nicht für Konvertierungen verwendet (SQL Server ebenfalls nicht) Konvertierung INT
in zulassen DATE
, der OLEDB-Treiber erledigt dies jedoch erfolgreich (wie in einem der Tests gezeigt).
Ich habe drei Tests durchgeführt. Für die beiden, die erfolgreich waren, habe ich mir die XML-Ausführungspläne angesehen, die die Abfrage zeigen, die remote ausgeführt wird. Für alle drei habe ich Ausnahmen oder OLEDB-Ereignisse über SQL Profiler erfasst:
Veranstaltungen:
- Fehler und Warnungen
- Beachtung
- Ausnahme
- Ausführungswarnungen
- Benutzerfehlermeldung
- OLEDB
- TSQL
- alle außer :
- SQL: StmtRecompile
- Statischer XQuery-Typ
Spaltenfilter:
- Anwendungsname
- NICHT WIE % Intellisense%
- SPID
- Größer als oder gleich 50
DIE TESTS
Test 1
CAST(NULL AS UNIQUEIDENTIFIER)
das funktioniert
SELECT TOP (2) CAST(NULL AS UNIQUEIDENTIFIER) AS [Something]
, (SELECT COUNT(*) FROM sys.[data_spaces]) AS [lcl]
FROM [Local].[TEMPTEST].[sys].[objects] rmt;
Relevanter Teil des XML-Ausführungsplans:
<DefinedValue>
<ColumnReference Column="Expr1002" />
<ScalarOperator ScalarString="NULL">
<Const ConstValue="NULL" />
</ScalarOperator>
</DefinedValue>
...
<RemoteQuery RemoteSource="Local" RemoteQuery=
"SELECT 1 FROM "TEMPTEST"."sys"."objects" "Tbl1001""
/>
Test 2
CAST(NULL AS UNIQUEIDENTIFIER)
das scheitert
SELECT TOP (2) CAST(NULL AS UNIQUEIDENTIFIER) AS [Something]
-- , (SELECT COUNT(*) FROM sys.[data_spaces]) AS [lcl]
FROM [Local].[TEMPTEST].[sys].[objects] rmt;
(Anmerkung: Ich habe die Unterabfrage dort gespeichert und auskommentiert, damit es einen Unterschied weniger gibt, wenn ich die XML-Tracedateien vergleiche.)
Test 3
CAST(NULL AS DATE)
das funktioniert
SELECT TOP (2) CAST(NULL AS DATE) AS [Something]
-- , (SELECT COUNT(*) FROM sys.[data_spaces]) AS [lcl]
FROM [Local].[TEMPTEST].[sys].[objects] rmt;
(Anmerkung: Ich habe die Unterabfrage dort gespeichert und auskommentiert, damit es einen Unterschied weniger gibt, wenn ich die XML-Tracedateien vergleiche.)
Relevanter Teil des XML-Ausführungsplans:
<DefinedValue>
<ColumnReference Column="Expr1002" />
<ScalarOperator ScalarString="[Expr1002]">
<Identifier>
<ColumnReference Column="Expr1002" />
</Identifier>
</ScalarOperator>
</DefinedValue>
...
<RemoteQuery RemoteSource="Local" RemoteQuery=
"SELECT TOP (2) NULL "Expr1002" FROM "TEMPTEST"."sys"."objects" "Tbl1001""
/>
Wenn Sie sich Test Nr. 3 ansehen, handelt es sich um einen Test SELECT TOP (2) NULL
auf dem "Remote" -System. Die SQL Profiler-Ablaufverfolgung zeigt, dass der Datentyp dieses Remote-Felds tatsächlich ist INT
. Die Ablaufverfolgung zeigt auch, dass das Feld auf der Clientseite (dh von wo aus ich die Abfrage ausführe) DATE
wie erwartet ist. Die Konvertierung von INT
nach DATE
, die in SQL Server einen Fehler verursacht, funktioniert im OLEDB-Treiber einwandfrei. Der entfernte Wert ist NULL
, also wird er direkt zurückgegeben, daher der <ColumnReference Column="Expr1002" />
.
Wenn Sie sich Test Nr. 1 ansehen, handelt es sich um einen Test SELECT 1
auf dem "Remote" -System. Die SQL Profiler-Ablaufverfolgung zeigt, dass der Datentyp dieses Remote-Felds tatsächlich ist INT
. Die Ablaufverfolgung zeigt auch, dass das Feld auf der Clientseite (dh von wo aus ich die Abfrage ausführe) GUID
wie erwartet ist. Die Konvertierung von INT
nach GUID
(denken Sie daran, dies erfolgt innerhalb des Treibers, und OLEDB nennt es "GUID"), was in SQL Server einen Fehler hervorruft, funktioniert innerhalb des OLEDB-Treibers einwandfrei. Der entfernte Wert ist nicht NULL
, daher wird er durch ein Literal ersetzt NULL
, daher der <Const ConstValue="NULL" />
.
Test Nr. 2 schlägt fehl, sodass kein Ausführungsplan vorhanden ist. Das "entfernte" System wird jedoch erfolgreich abgefragt, die Ergebnismenge kann jedoch nicht zurückgegeben werden. Die Abfrage, die SQL Profiler erfasst hat, lautet:
SELECT TOP (2) NULL "Expr1002" FROM "TEMPTEST"."sys"."objects" "Tbl1001"
Das ist genau die gleiche Abfrage, die in Test 1 durchgeführt wird, aber hier schlägt sie fehl. Es gibt noch andere kleine Unterschiede, aber ich kann die OLEDB-Mitteilung nicht vollständig interpretieren. Das entfernte Feld wird jedoch weiterhin als INT
(wType = 3 = adInteger / Vier-Byte-Ganzzahl mit Vorzeichen / DBTYPE_I4) angezeigt, während das Feld "client" weiterhin als GUID
(wType = 72 = adGUID / global eindeutiger Bezeichner / DBTYPE_GUID) angezeigt wird. Die OLE DB-Dokumentation hilft nicht viel, da GUID-Datentypkonvertierungen , DBDATE-Datentypkonvertierungen und I4-Datentypkonvertierungen zeigen, dass die Konvertierung von I4 in GUID oder DBDATE nicht unterstützt wird, die DATE
Abfrage jedoch funktioniert.
Die Trace-XML-Dateien für die drei Tests befinden sich in PasteBin. Wenn Sie die Details sehen möchten, in denen sich die einzelnen Tests von den anderen unterscheiden, können Sie sie lokal speichern und dann "diff" auf sie anwenden. Die Dateien sind:
- NullGuidSuccess.xml
- NullGuidError.xml
- NullDateSuccess.xml
ERGO?
Was tun? Wahrscheinlich nur die Problemumgehung, die ich im oberen Abschnitt notiert habe, SQLNCLI11
da der SQL Server Native Client - - ab SQL Server 2012 veraltet ist. Die meisten MSDN-Seiten zum Thema SQL Server Native Client haben den folgenden Hinweis am oben:
Warnung
SQL Server Native Client (SNAC) wird nach SQL Server 2012 nicht mehr unterstützt. Vermeiden Sie die Verwendung von SNAC in neuen Entwicklungsarbeiten, und planen Sie, Anwendungen zu ändern, die es derzeit verwenden. Der Microsoft ODBC-Treiber für SQL Server bietet native Konnektivität von Windows zu Microsoft SQL Server und zur Microsoft Azure SQL-Datenbank.
Weitere Informationen finden Sie unter:
ODBC ??
Ich richte einen ODBC-Verbindungsserver ein über:
EXEC master.dbo.sp_addlinkedserver
@server = N'LocalODBC',
@srvproduct=N'{my_server_name}',
@provider=N'MSDASQL',
@provstr=N'Driver={SQL Server};Server=(local);Trusted_Connection=Yes;';
EXEC master.dbo.sp_addlinkedsrvlogin
@rmtsrvname=N'LocalODBC',
@useself=N'True',
@locallogin=NULL,
@rmtuser=NULL,
@rmtpassword=NULL;
Und dann versucht:
SELECT CAST(NULL AS UNIQUEIDENTIFIER) AS [Something]
FROM [LocalODBC].[tempdb].[sys].[objects] rmt;
und erhielt den folgenden Fehler:
Der OLE DB-Anbieter "MSDASQL" für den Verbindungsserver "LocalODBC" hat die Meldung "Die angeforderte Konvertierung wird nicht unterstützt." Zurückgegeben.
Meldung 7341, Ebene 16,
Status 2, Zeile 53 Der aktuelle Zeilenwert der Spalte "(vom Benutzer generierter Ausdruck) .Expr1002" kann nicht vom OLE DB-Anbieter "MSDASQL" für den Verbindungsserver "LocalODBC" abgerufen werden.
PS
Beim Transport von GUIDs zwischen entfernten und lokalen Servern werden Nicht-NULL-Werte über eine spezielle Syntax behandelt. Ich habe beim Ausführen die folgenden OLE DB-Ereignisinformationen in der SQL Profiler-Ablaufverfolgung festgestellt CAST(0x00 AS UNIQUEIDENTIFIER)
:
<RemoteQuery RemoteSource="Local" RemoteQuery=
"SELECT {guid'00000000-0000-0000-0000-000000000000'} "Expr1002" FROM "TEMPTEST"."sys"."objects" "Tbl1001""
/>
PPS
Ich habe auch über OPENQUERY
mit der folgenden Abfrage getestet :
SELECT TOP (2) CAST(NULL AS UNIQUEIDENTIFIER) AS [Something]
--, (SELECT COUNT(*) FROM sys.[data_spaces]) AS [lcl]
FROM OPENQUERY([Local], N'SELECT 705 AS [dummy] FROM [TEMPTEST].[sys].[objects];') rmt;
und es gelang, auch ohne die lokale Objektreferenz. Die SQL Profiler-Trace-XML-Datei wurde in PasteBin gepostet unter:
NullGuidSuccessOPENQUERY.xml
Der XML-Ausführungsplan zeigt dies unter Verwendung einer NULL
Konstante wie in Test 1.