Pytam dane z połączonego serwera poprzez widok na serwerze źródłowym. Widok musi zawierać kilka standardowych kolumn, takich jak Created
, Modified
i Deleted
, ale w tym przypadku tabela na serwerze źródłowym nie ma odpowiedniej informacji. Kolumny są zatem jawnie rzutowane na odpowiadające im typy. Zaktualizowałem widok, zmieniając kolumnę z
NULL AS Modified
do
CAST(NULL as DateTime) as Modified
Jednak po wykonaniu tej aktualizacji widok powoduje wyświetlenie następującego komunikatu o błędzie:
Msg 7341, poziom 16, stan 2, wiersz 3 Nie można pobrać bieżącej wartości wiersza kolumny „(wyrażenie wygenerowane przez użytkownika) .Expr1002” od dostawcy OLE DB „SQLNCLI11” dla połączonego serwera „”.
Zrobiliśmy tę „jawną obsadę” - ogólnie bez problemu, na serwerze źródłowym i podejrzewam, że problem może być związany z wersją zaangażowanych serwerów. Tak naprawdę nie musimy stosować tej obsady, ale wydaje się ona czystsza. W tej chwili jestem ciekawy, dlaczego tak się dzieje.
Wersja serwera (pochodzenie):
Microsoft SQL Server 2012 - 11.0.5058.0 (X64) 14 maja 2014 18:34:29 Prawa autorskie (c) Microsoft Corporation Enterprise Edition (64-bit) w systemie Windows NT 6.1 (kompilacja 7601: Service Pack 1) (Hypervisor)
Wersja serwera (połączona):
Microsoft SQL Server 2008 R2 (SP1) - 10.50.2500.0 (X64) 17 czerwca 2011 00:54:03 Prawa autorskie (c) Microsoft Corporation Enterprise Edition (64-bit) na Windows NT 6.1 (kompilacja 7601: Service Pack 1) (Hypervisor )
Edytuj
Właśnie zdałem sobie sprawę, że popełniłem błąd, nie publikując wszystkich omawianych kolumn, i muszę przeprosić za pominięcie ważnego szczegółu. Nie wiem, jak wcześniej tego nie zauważyłem. Pozostaje jednak pytanie.
Błędne rzutowanie nie dzieje się z rzutowaniem na DateTime, ale z rzutowaniem kolumny na UniqueIdentifier.
Oto sprawca:
CAST(NULL AS UniqueIdentifier) AS [GUID]
Unikalne identyfikatory są obsługiwane w SQL Server 2008 R2 i jak wspomniano w komentarzach, zapytanie wykonane przez widok działa poprawnie na połączonym serwerze.
Danish_Norwegian_CI_AS
a serwer połączony ma sortowanieSQL_Danish_Pref_CP1_CI_AS
, aleCOLLATE
klauzula nie może być zastosowana doDateTime
kolumn, więc nie posunąłem się znacznie dalej!select Null from ...
w Z lub zagnieżdżone zapytanie iCAST
w innym?INT
zmiana typu danych w ten sposób. Nie wiem jednak, dlaczego to dałoby ci ten komunikat o błędzie.Odpowiedzi:
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:To faktycznie działa. Ale następujący błąd otrzymuje oryginalny błąd:
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żeNULL
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
naUNIQUEIDENTIFIER
(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
doDATE
, 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:
Filtry kolumnowe:
TESTY
Test 1
CAST(NULL AS UNIQUEIDENTIFIER)
to działaOdpowiednia część planu wykonania XML:
Test 2
CAST(NULL AS UNIQUEIDENTIFIER)
to się nie udaje(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(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:
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ściINT
. Śledzenie pokazuje również, że pole po stronie klienta (tj. Z którego uruchamiam zapytanie) jestDATE
zgodne z oczekiwaniami. Konwersja zINT
naDATE
coś, co spowoduje błąd w SQL Server, działa dobrze w sterowniku OLEDB. Zdalna wartość jestNULL
, 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ściINT
. Śledzenie pokazuje również, że pole po stronie klienta (tj. Z którego uruchamiam zapytanie) jestGUID
zgodne z oczekiwaniami. Konwersja zINT
naGUID
(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 jestNULL
, więc jest zastąpiona literałemNULL
, 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:
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ę jakoGUID
(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, jednakDATE
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:
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:Aby uzyskać więcej informacji, zobacz:
ODBC ??
Skonfigurowałem serwer ODBC podłączony przez:
A potem próbował:
i otrzymał następujący błąd:
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)
:PPS
Testowałem również za
OPENQUERY
pomocą następującego zapytania: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.źródło
Jest tylko brzydkie obejście - użyj stałej stałej daty, takiej jak
'1900-01-01'
zamiastnull
.Po zaimportowaniu możesz zaktualizować kolumny z
1900-01-01
powrotem do Null.Jest to rodzaj funkcji / błędu SQL 2012, jak tutaj .
Edycja: zastąpiono
1900-00-00
prawidłową datą1900-01-01
zgodnie z komentarzem @a_horse_w_no_name poniżej.źródło
uniqueidentifier
kolumna. A może można go dostosować - może coś takiegoCAST('00000000-0000-0000-0000-000000000000' AS UniqueIdentifier) AS [GUID]
?1900-00-00
jest niepoprawną datą i nie zostanie zaakceptowana.Problem dotyczy konwersji typu danych (jak podano w komentarzach).
Rozważ następujące:
Zauważ, że
NullColumn
jest typuint
. SQL Server nie lubi konwertowaćint
wartości nauniqueidentifier
. TaSELECT
instrukcja nie powiedzie się podczas konwersji typu danych:Podczas gdy tę konkretną wartość (NULL) można rzutować na GUID, SQL Server zgłasza błąd na podstawie konwersji typu danych, zanim nawet przyjrzy się konkretnym wartościom. Zamiast tego będziesz musiał wykonać wieloetapową
CAST
operację, aby zmienić domyślnyint
typ danych, który można przekształcić w czystyuniqueidentifer
sposób - co oznacza najpierw rzutowanie,varchar
a następnieuniqueidentifier
:źródło
PO może ostatecznie zdecydować, czy jest to odpowiednia odpowiedź.
Nie mam „absolutnego” dowodu, ale „podejrzewam”, że problem wynika z faktu, że UniqueIdentifer jest zależny od serwera i być może dostawca ma trudności z ustaleniem, z którego serwera (lokalnego lub zdalnego) można uzyskać ten unikatowy identyfikator, mimo że jest to zero. Dlatego prawdopodobnie możesz rzucić dowolny inny typ danych w tym scenariuszu, ale nie unikalny identyfikator. Typy danych, które są zależne od „serwera”, takie jak UNIQUEIDENTIFIERS i DATETIMEOFFSET, dają napotkany błąd.
Używanie OPENQUERY zamiast 4-częściowej nazwy działa.
źródło
Uniqueidentifier
iDateTimeOffset
będąc uzależnionym serwera? Czy masz na to jakieś źródła?Obejście: zaakceptowana odpowiedź wydaje się wskazywać, że konwersja musi nastąpić lokalnie, ponieważ sterownik OLEDB nie obsługuje jej.
Myślę więc, że prostym obejściem (przynajmniej w przypadku mojego zapytania, które polega na wybraniu wartości null
uniqueidentifier
w podstawowym przypadku rekurencyjnej CTE) jest zadeklarowanie zmiennej null:źródło