因此,我意识到在CAST
本地而不是在远程实例上完成操作后,便能够重现该错误。我以前曾建议升级到SP3,以期解决此问题(部分原因是由于无法重现SP3上的错误,部分原因是无论如何它都是一个好主意)。但是,既然我可以重现该错误,那么很明显,升级到SP3(虽然可能仍然是一个好主意)将无法解决此问题。我还重现了SQL Server 2008 R2 RTM和2014 SP1中的错误(在所有三种情况下均使用“回送”本地链接服务器)。
看来,这个问题与做在那里执行查询时,或至少其中一部分(S)的它正在执行。我之所以这样说是因为我能够使该CAST
操作生效,但是只能通过引用本地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);
确实有效。但是以下内容得到了原始错误:
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 Native Client / OLEDB驱动程序内。但是,转换错误发生在OLEDB驱动程序内,因此不一定是从转换INT
为UNIQUEIDENTIFIER
(SQL Server不允许的转换)的问题,因为驱动程序未使用SQL Server进行转换(SQL Server也不会允许转换INT
为DATE
,但OLEDB驱动程序成功处理了该操作,如其中一项测试所示)。
我进行了三项测试。对于成功的两个,我查看了XML执行计划,这些计划显示了正在远程执行的查询。对于这三个,我都通过SQL Profiler捕获了任何Exception或OLEDB事件:
大事记:
- 错误和警告
- OLEDB
- TSQL
- 所有的除外:
- SQL:StmtRecompile
- XQuery静态类型
列过滤器:
测试
测试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
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 SQLNCLI11
Server 2012起不推荐使用SQL本机客户端-可能只是我在上一节中提到的解决方法。有关SQL Server本机客户端主题的大多数MSDN页面在以下内容中均具有以下注意事项:最佳:
警告
SQL Server 2012以后不支持SQL Server Native Client(SNAC)。请避免在新的开发工作中使用SNAC,并计划修改当前使用它的应用程序。用于SQL Server的Microsoft ODBC驱动程序提供从Windows到Microsoft SQL Server和Microsoft Azure SQL数据库的本机连接。
有关更多信息,请参见:
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;
并收到以下错误:
链接服务器“ LocalODBC”的OLE DB提供程序“ MSDASQL”返回消息“不支持请求的转换”。
消息7341,级别16,状态2,第53
行无法从链接服务器“ LocalODBC”的OLE DB提供程序“ MSDASQL”获取列“(用户生成的表达式).Expr1002”的当前行值。
聚苯乙烯
与在远程服务器和本地服务器之间传输GUID有关,非NULL值通过特殊语法处理。运行时,我在SQL事件探查器跟踪中注意到以下OLE DB事件信息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;
即使没有本地对象引用,它也成功。SQL Profiler跟踪XML文件已发布到PasteBin,网址为:
NullGuidSuccessOPENQUERY.xml
XML执行计划使用NULL
常量来显示它,与测试1中的一样。