Tak więc byłem w stanie odtworzyć błąd po uświadomieniu sobie, CAST
że robiono to lokalnie, a nie w zdalnej instancji. Wcześniej zalecałem przejście na SP3 w nadziei, że to naprawię (częściowo z powodu niemożności odtworzenia błędu w SP3, a częściowo z tego powodu, że jest to dobry pomysł niezależnie). Jednak teraz, kiedy mogę odtworzyć błąd, jasne jest, że przejście na SP3, choć wciąż prawdopodobnie dobry pomysł, nie naprawi tego. Ponadto odtworzyłem błąd w SQL Server 2008 R2 RTM i 2014 SP1 (przy użyciu lokalnego połączonego serwera „loop-back” we wszystkich trzech przypadkach).
Wygląda na to, że problem ten dotyczy miejsca wykonywania zapytania lub przynajmniej jego części . Mówię to, ponieważ udało mi się uruchomić CAST
operację, ale tylko poprzez dołączenie odwołania do lokalnego obiektu DB:
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);
To faktycznie działa. Ale następujący błąd otrzymuje oryginalny błąd:
SELECT rmt.*, CAST(NULL AS UNIQUEIDENTIFIER) AS [GUID]
FROM [Local].[database_name].[dbo].[table_name] rmt
CROSS JOIN (VALUES (1)) tmp(dummy);
Zgaduję, że gdy nie ma lokalnych odwołań, całe zapytanie jest wysyłane do zdalnego systemu, który ma zostać wykonany, i z jakiegoś powodu NULL
nie można go przekonwertować UNIQUEIDENTIFIER
, a może NULL
niepoprawnie jest tłumaczony przez sterownik OLE DB.
Na podstawie testów, które przeprowadziłem, wydaje się, że to błąd, ale nie jestem pewien, czy błąd dotyczy SQL Server, czy SQL Server Native Client / OLEDB. Jednak błąd konwersji występuje w sterowniku OLEDB, a zatem niekoniecznie jest to problem z konwersją z INT
na UNIQUEIDENTIFIER
(konwersja, która nie jest dozwolona w SQL Server), ponieważ sterownik nie używa SQL Server do konwersji (SQL Server również nie pozwalają na konwersję INT
do DATE
, ale sterownik OLEDB radzi sobie z tym pomyślnie, jak pokazano w jednym z testów).
Przeprowadziłem trzy testy. W przypadku dwóch, które się powiodły, spojrzałem na plany wykonania XML, które pokazują zdalne wykonywanie zapytania. Dla wszystkich trzech przechwyciłem wszelkie wyjątki lub zdarzenia OLEDB za pośrednictwem SQL Profiler:
Wydarzenia:
- Błędy i ostrzeżenia
- Uwaga
- Wyjątek
- Ostrzeżenia dotyczące wykonania
- Komunikat o błędzie użytkownika
- OLEDB
- TSQL
- wszystkie z wyjątkiem :
- SQL: StmtRecompile
- Typ statyczny XQuery
Filtry kolumnowe:
TESTY
Test 1
CAST(NULL AS UNIQUEIDENTIFIER)
to działa
SELECT TOP (2) CAST(NULL AS UNIQUEIDENTIFIER) AS [Something]
, (SELECT COUNT(*) FROM sys.[data_spaces]) AS [lcl]
FROM [Local].[TEMPTEST].[sys].[objects] rmt;
Odpowiednia część planu wykonania XML:
<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)
to się nie udaje
SELECT TOP (2) CAST(NULL AS UNIQUEIDENTIFIER) AS [Something]
-- , (SELECT COUNT(*) FROM sys.[data_spaces]) AS [lcl]
FROM [Local].[TEMPTEST].[sys].[objects] rmt;
(uwaga: zachowałem tam podzapytanie, skomentowałem, aby różnica była mniejsza o jedną, gdy porównałem pliki śledzenia XML)
Test 3
CAST(NULL AS DATE)
to działa
SELECT TOP (2) CAST(NULL AS DATE) AS [Something]
-- , (SELECT COUNT(*) FROM sys.[data_spaces]) AS [lcl]
FROM [Local].[TEMPTEST].[sys].[objects] rmt;
(uwaga: zachowałem tam podzapytanie, skomentowałem, aby różnica była mniejsza o jedną, gdy porównałem pliki śledzenia XML)
Odpowiednia część planu wykonania XML:
<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""
/>
Jeśli spojrzysz na test nr 3, robi SELECT TOP (2) NULL
on „zdalny” system. Śledzenie SQL Profiler pokazuje, że typ danych tego zdalnego pola jest w rzeczywistości INT
. Śledzenie pokazuje również, że pole po stronie klienta (tj. Z którego uruchamiam zapytanie) jest DATE
zgodne z oczekiwaniami. Konwersja z INT
na DATE
coś, co spowoduje błąd w SQL Server, działa dobrze w sterowniku OLEDB. Zdalna wartość jest NULL
, więc jest zwracana bezpośrednio, stąd <ColumnReference Column="Expr1002" />
.
Jeśli spojrzysz na test nr 1, robi SELECT 1
on „zdalny” system. Śledzenie SQL Profiler pokazuje, że typ danych tego zdalnego pola jest w rzeczywistości INT
. Śledzenie pokazuje również, że pole po stronie klienta (tj. Z którego uruchamiam zapytanie) jest GUID
zgodne z oczekiwaniami. Konwersja z INT
na GUID
(pamiętaj, że odbywa się to w sterowniku, a OLEDB nazywa to „GUID”), coś, co spowoduje błąd w SQL Server, działa dobrze w sterowniku OLEDB. Zdalna wartość nie jest NULL
, więc jest zastąpiona literałem NULL
, stąd <Const ConstValue="NULL" />
.
Test nr 2 kończy się niepowodzeniem, więc nie ma planu wykonania. Jednak pomyślnie wysyła zapytanie do „zdalnego” systemu, ale po prostu nie może przekazać zestawu wyników. Zapytanie przechwycone przez SQL Profiler to:
SELECT TOP (2) NULL "Expr1002" FROM "TEMPTEST"."sys"."objects" "Tbl1001"
To jest dokładnie to samo zapytanie, które jest wykonywane w teście nr 1, ale tutaj się nie udaje. Istnieją inne drobne różnice, ale nie mogę w pełni zinterpretować komunikacji OLEDB. Jednak zdalne pole nadal pokazuje się jako INT
(wType = 3 = adInteger / czterobajtowa liczba całkowita ze znakiem / DBTYPE_I4), podczas gdy pole „client” wciąż pokazuje się jako GUID
(wType = 72 = adGUID / globalnie unikalny identyfikator / DBTYPE_GUID). Dokumentacja OLE DB nie pomaga dużo jak GUID konwersji typów danych , DBDATE konwersji typów danych oraz I4 konwersji typów danych pokazują, że konwersja z I4 albo GUID lub DBDATE jest obsługiwany, jednak DATE
prace zapytań.
Pliki XML śledzenia dla trzech testów znajdują się w PasteBin. Jeśli chcesz zobaczyć szczegóły różnic między poszczególnymi testami, możesz zapisać je lokalnie, a następnie wykonać na nich różnicę. Pliki to:
- NullGuidSuccess.xml
- NullGuidError.xml
- NullDateSuccess.xml
ERGO?
Co z tym zrobić? Prawdopodobnie tylko obejście, które zauważyłem w górnej części, biorąc pod uwagę, że SQL Native Client - SQLNCLI11
- jest przestarzały od SQL Server 2012. Większość stron MSDN na temat SQL Server Native Client ma następujące powiadomienie na Top:
Ostrzeżenie
SQL Server Native Client (SNAC) nie jest obsługiwany po SQL Server 2012. Unikaj używania SNAC w nowych pracach programistycznych i planuj modyfikację aplikacji, które obecnie go używają. Sterownik Microsoft ODBC dla SQL Server zapewnia natywną łączność z Windows do Microsoft SQL Server i Microsoft Azure SQL Database.
Aby uzyskać więcej informacji, zobacz:
ODBC ??
Skonfigurowałem serwer ODBC podłączony przez:
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;
A potem próbował:
SELECT CAST(NULL AS UNIQUEIDENTIFIER) AS [Something]
FROM [LocalODBC].[tempdb].[sys].[objects] rmt;
i otrzymał następujący błąd:
Dostawca OLE DB „MSDASQL” dla połączonego serwera „LocalODBC” zwrócił komunikat „Żądana konwersja nie jest obsługiwana.”
Msg 7341, poziom 16, stan 2, wiersz 53
Nie można pobrać bieżącej wartości wiersza kolumny „(wyrażenie wygenerowane przez użytkownika) .Expr1002” od dostawcy OLE DB „MSDASQL” dla połączonego serwera „LocalODBC”.
PS
Ponieważ dotyczy to transportu identyfikatorów GUID między serwerami zdalnymi i lokalnymi, wartości inne niż NULL są obsługiwane za pomocą specjalnej składni. Podczas uruchamiania zauważyłem następujące informacje o zdarzeniu OLE DB w śledzeniu SQL Profiler CAST(0x00 AS UNIQUEIDENTIFIER)
:
<RemoteQuery RemoteSource="Local" RemoteQuery=
"SELECT {guid'00000000-0000-0000-0000-000000000000'} "Expr1002" FROM "TEMPTEST"."sys"."objects" "Tbl1001""
/>
PPS
Testowałem również za OPENQUERY
pomocą następującego zapytania:
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;
i udało się, nawet bez odniesienia do lokalnego obiektu. Plik XML śledzenia SQL Profiler został wysłany do PasteBin pod adresem:
NullGuidSuccessOPENQUERY.xml
Plan wykonania XML pokazuje to przy użyciu NULL
stałej, takiej samej jak w teście nr 1.