Отже, я зміг відтворити помилку, зрозумівши, що це CAST
робиться локально, а не на віддаленому екземплярі. Раніше я рекомендував перейти до SP3 з надією виправити це (частково через те, що не вдалося відтворити помилку на SP3, а частково через те, що вона була гарною ідеєю незалежно). Однак тепер, коли я можу відтворити помилку, зрозуміло, що перехід до SP3, хоча, мабуть, і гарна ідея, це не виправить. А також я відтворив помилку в SQL Server 2008 R2 RTM та 2014 SP1 (використовуючи локальний пов'язаний сервер "петля назад" у всіх трьох випадках).
Здається , що ця проблема пов'язана з , де запит виконується, або , по крайней мере там , де частина (частини) з нього виконуються. Я говорю це тому, що мені вдалося змусити CAST
операцію працювати, але лише включивши посилання на локальний об’єкт БД:
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);
Це насправді працює. Але наступне отримує початкову помилку:
SELECT rmt.*, CAST(NULL AS UNIQUEIDENTIFIER) AS [GUID]
FROM [Local].[database_name].[dbo].[table_name] rmt
CROSS JOIN (VALUES (1)) tmp(dummy);
Я здогадуюсь, що коли немає локальних посилань, весь запит відправляється у віддалену систему, яку потрібно виконати, і чомусь NULL
s не може бути перетворений UNIQUEIDENTIFIER
, або, можливо, NULL
це перекладається драйвером OLE DB неправильно.
На основі тестування, яке я зробив, це виявилося б помилкою, але я не впевнений, що помилка знаходиться в SQL Server або драйвері OLEDB для Native Client SQL Server. Однак помилка перетворення виникає в драйвері OLEDB, і тому не обов'язково виникає проблема перетворення з INT
на UNIQUEIDENTIFIER
(конверсія, яка не дозволена в SQL Server), оскільки драйвер не використовує SQL Server для здійснення конверсій (SQL Server також не робить дозволяють перетворювати INT
в DATE
, але ручки водія OLEDB , який успішно, як показано в одному з тестів).
Я провів три випробування. Для двох, що досягли успіху, я переглянув плани виконання XML, які показують запит, який виконується віддалено. Для всіх трьох я зафіксував будь-які винятки або події OLEDB через SQL Profiler:
Події:
- Помилки та попередження
- Увага
- Виняток
- Попередження щодо виконання
- Повідомлення про помилку користувача
- OLEDB
- TSQL
- все, крім :
- SQL: StmtRecompile
- Статичний тип XQuery
Фільтри стовпців:
- ApplicationName
- НЕ ПОДОБАЙТЕ % Intellisense%
- SPID
ТЕСТИ
Тест 1
CAST(NULL AS UNIQUEIDENTIFIER)
що працює
SELECT TOP (2) CAST(NULL AS UNIQUEIDENTIFIER) AS [Something]
, (SELECT COUNT(*) FROM sys.[data_spaces]) AS [lcl]
FROM [Local].[TEMPTEST].[sys].[objects] rmt;
Відповідна частина плану виконання XML:
<DefinedValue>
<ColumnReference Column="Expr1002" />
<ScalarOperator ScalarString="NULL">
<Const ConstValue="NULL" />
</ScalarOperator>
</DefinedValue>
...
<RemoteQuery RemoteSource="Local" RemoteQuery=
"SELECT 1 FROM "TEMPTEST"."sys"."objects" "Tbl1001""
/>
Тест 2
CAST(NULL AS UNIQUEIDENTIFIER)
що провалюється
SELECT TOP (2) CAST(NULL AS UNIQUEIDENTIFIER) AS [Something]
-- , (SELECT COUNT(*) FROM sys.[data_spaces]) AS [lcl]
FROM [Local].[TEMPTEST].[sys].[objects] rmt;
(зауважте: я зберігав підзапит там, прокоментував, так що це буде одна менша різниця, коли я порівнював XML-файли слідів)
Тест 3
CAST(NULL AS DATE)
що працює
SELECT TOP (2) CAST(NULL AS DATE) AS [Something]
-- , (SELECT COUNT(*) FROM sys.[data_spaces]) AS [lcl]
FROM [Local].[TEMPTEST].[sys].[objects] rmt;
(зауважте: я зберігав підзапит там, прокоментував, так що це буде одна менша різниця, коли я порівнював XML-файли слідів)
Відповідна частина плану виконання 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""
/>
Якщо ви подивитесь на Тест №3, він робить " SELECT TOP (2) NULL
на" віддаленій системі. Трасування SQL Profiler показує, що тип даних цього віддаленого поля насправді є INT
. Слід також показує, що поле на стороні клієнта (тобто звідки я запускаю запит) є DATE
, як очікувалося. Перетворення з INT
на DATE
, те, що стане помилкою в SQL Server, добре працює в драйвері OLEDB. Віддалене значення є NULL
, тому воно повертається безпосередньо, звідси і <ColumnReference Column="Expr1002" />
.
Якщо ви подивитесь на тест №1, він робить « SELECT 1
у» віддаленій системі. Трасування SQL Profiler показує, що тип даних цього віддаленого поля насправді є INT
. Слід також показує, що поле на стороні клієнта (тобто звідки я запускаю запит) є GUID
, як очікувалося. Перетворення з INT
на GUID
(пам'ятайте, це робиться в драйвері, і OLEDB називає це "GUID"), те, що отримає помилку в SQL Server, працює добре в драйвері OLEDB. Віддаленого значення немає NULL
, тому його замінюють на буквальне NULL
, отже, і значення <Const ConstValue="NULL" />
.
Тест №2 провалюється, тому немає плану виконання. Однак він запитує "віддалену" систему успішно, але просто не може повернути набір результатів. Запит, який захопив SQL Profiler:
SELECT TOP (2) NULL "Expr1002" FROM "TEMPTEST"."sys"."objects" "Tbl1001"
Це той самий запит, який робиться в Тесті №1, але тут він не працює. Існують і інші незначні відмінності, але я не можу повністю інтерпретувати зв'язок OLEDB. Однак віддалене поле все ще відображається як INT
(wType = 3 = adInteger / чотирибайтове підписане ціле число / DBTYPE_I4), а поле "клієнт" все ще відображається як GUID
(wType = 72 = adGUID / глобально унікальний ідентифікатор / DBTYPE_GUID). Документації OLE DB не надто допомагає , як GUID типу даних перетворень , DBDATE Тип даних перетворень і I4 Тип даних перетворень показують , що перетворення з I4 або GUID або DBDATE не підтримується, все ж DATE
роботи запиту.
Файли Trace XML для трьох тестів розташовані на PasteBin. Якщо ви хочете побачити деталі того, чим кожен тест відрізняється від інших, можете зберегти їх локально, а потім зробити "розріз" на них. Файли:
- NullGuidSuccess.xml
- NullGuidError.xml
- NullDateSuccess.xml
ERGO?
Що з цим робити? Мабуть, якраз обхід, який я зазначив у верхньому розділі, враховуючи, що SQL Native Client - SQLNCLI11
- застаріло, як у SQL Server 2012. Більшість сторінок MSDN на тему Stive Server Native Client мають таке повідомлення в верх:
Увага
Нативний клієнт SQL Server (SNAC) не підтримується за межами SQL Server 2012. Уникайте використання SNAC у нових роботах з розробки та плануйте модифікувати додатки, які його зараз використовують. Драйвер Microsoft ODBC для SQL Server забезпечує вбудовану можливість з'єднання з Windows , в Microsoft SQL Server і Microsoft Azure SQL Database.
Для отримання додаткової інформації дивіться:
ODBC ??
Я встановив ODBC-пов'язаний сервер за допомогою:
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;
А потім спробував:
SELECT CAST(NULL AS UNIQUEIDENTIFIER) AS [Something]
FROM [LocalODBC].[tempdb].[sys].[objects] rmt;
і отримав таку помилку:
Провайдер OLE DB "MSDASQL" для пов'язаного сервера "LocalODBC" повернув повідомлення "Запрошена конверсія не підтримується."
Msg 7341, Рівень 16, Стан 2, Рядок 53
Неможливо отримати поточне значення рядка стовпця "(вираз, створений користувачем) .Expr1002" від постачальника OLE DB "MSDASQL" для пов'язаного сервера "LocalODBC".
PS
Оскільки це стосується транспортування GUID між віддаленими та локальними серверами, не-NULL значення обробляються за допомогою спеціального синтаксису. Під час запуску я помітив таку інформацію про подію OLE DB у сліді SQL Profiler CAST(0x00 AS UNIQUEIDENTIFIER)
:
<RemoteQuery RemoteSource="Local" RemoteQuery=
"SELECT {guid'00000000-0000-0000-0000-000000000000'} "Expr1002" FROM "TEMPTEST"."sys"."objects" "Tbl1001""
/>
PPS
Я також перевірив за OPENQUERY
допомогою наступного запиту:
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;
і це вдалося навіть без посилання на місцевий об’єкт. XML-файл трасування SQL Profiler був опублікований в PasteBin за адресою:
NullGuidSuccessOPENQUERY.xml
План виконання XML показує його за допомогою NULL
константи, такий же, як у Тесті №1.