Итак, я смог воспроизвести ошибку, поняв, что 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 или в драйвере собственного клиента SQL Server / OLEDB. Однако ошибка преобразования происходит в драйвере OLEDB, и поэтому не обязательно является проблемой преобразования из INT
в UNIQUEIDENTIFIER
(преобразование, которое не разрешено в SQL Server), поскольку драйвер не использует SQL Server для выполнения преобразований (SQL Server также не делает этого). позволяют преобразовывать INT
в DATE
, но ручки водителя OLEDB , который успешно, как показано в одном из тестов).
Я провел три теста. Для двух успешных я посмотрел на планы выполнения XML, которые показывают запрос, который выполняется удаленно. Для всех трех я захватил любые исключения или события OLEDB через SQL Profiler:
Мероприятия:
- Ошибки и предупреждения
- Внимание
- исключение
- Предупреждения Исполнения
- Сообщение об ошибке пользователя
- OLEDB
- TSQL
- все кроме :
- SQL: StmtRecompile
- Статический тип XQuery
Фильтры колонок:
- Имя приложения
- НЕ НРАВИТСЯ % Intellisense%
- ИСП
ТЕСТЫ
Тест 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
работы запроса.
XML-файлы трассировки для трех тестов находятся в PasteBin. Если вы хотите увидеть детали того, где каждый тест отличается от других, вы можете сохранить их локально, а затем выполнить «сравнение» с ними. Файлы:
- NullGuidSuccess.xml
- NullGuidError.xml
- NullDateSuccess.xml
ЭРГО?
Что с этим делать? Вероятно, это просто обходной путь, который я отметил в верхнем разделе, учитывая, что собственный клиент SQL - SQLNCLI11
- не рекомендуется на SQL Server 2012. Большинство страниц MSDN по теме собственного клиента SQL Server имеют следующее уведомление на верх:
Предупреждение
Собственный клиент SQL Server (SNAC) не поддерживается после SQL Server 2012. Избегайте использования SNAC в новых разработках и планируйте модифицировать приложения, которые в настоящее время его используют. Драйвер Microsoft ODBC для SQL Server обеспечивает встроенную возможность соединения с Windows , в Microsoft SQL Server и Microsoft Azure SQL Database.
Для получения дополнительной информации, пожалуйста, смотрите:
ODBC ??
Я установил ODBC Linked Server через:
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» вернул сообщение «Запрошенное преобразование не поддерживается».
Сообщение 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.