我正在通过源服务器上的视图从链接服务器查询数据。视图必须包含几个标准化的列,例如和Created
,但在这种情况下,源服务器上的表没有任何合适的信息。因此,这些列被显式地转换为它们各自的类型。我更新了视图,将一列从Modified
Deleted
NULL AS Modified
至
CAST(NULL as DateTime) as Modified
但是,执行此更新后,视图会触发以下错误消息:
消息 7341,级别 16,状态 2,行 3 无法从链接服务器“”的 OLE DB 提供程序“SQLNCLI11”获取列“(用户生成的表达式).Expr1002”的当前行值。
我们已经完成了这种“显式转换”——通常在源服务器上进行更改而不用担心,我怀疑这个问题可能与所涉及的服务器的版本有关。我们真的不需要应用这个演员表,但感觉更干净。现在我只是好奇为什么会这样。
服务器版本(来源):
Microsoft SQL Server 2012 - 11.0.5058.0 (X64) 2014 年 5 月 14 日 18:34:29 版权所有 (c) Microsoft Corporation Enterprise Edition (64-bit) on Windows NT 6.1 (Build 7601: Service Pack 1) (Hypervisor)
服务器版本(链接):
Microsoft SQL Server 2008 R2 (SP1) - 10.50.2500.0 (X64) 2011 年 6 月 17 日 00:54:03 版权所有 (c) Microsoft Corporation Enterprise Edition (64-bit) on Windows NT 6.1 (Build 7601: Service Pack 1) (Hypervisor )
编辑
我刚刚意识到我犯了一个错误,没有发布所有有问题的专栏,我必须为遗漏一个重要细节而道歉。我不知道我怎么没有早点注意到这一点。不过,问题仍然存在。
转换为 DateTime 时不会发生错误转换,而是将列转换为 UniqueIdentifier。
这是罪魁祸首:
CAST(NULL AS UniqueIdentifier) AS [GUID]
SQL Server 2008 R2 支持 UniqueIdentifiers,正如评论中提到的,视图执行的查询在链接服务器上运行良好。
CAST
因此,在意识到这是在本地而不是在远程实例上完成之后,我能够重现该错误。我之前曾建议升级到 SP3,希望能解决这个问题(部分原因是无法在 SP3 上重现错误,部分原因是无论如何它都是一个好主意)。但是,现在我可以重现该错误,很明显升级到 SP3 虽然仍然可能是一个好主意,但并不能解决这个问题。而且我还重现了 SQL Server 2008 R2 RTM 和 2014 SP1 中的错误(在所有三种情况下都使用“环回”本地链接服务器)。似乎这个问题与查询的执行位置有关,或者至少与查询的部分执行位置有关。我这样说是因为我能够使
CAST
操作正常工作,但只能通过包含对本地 DB 对象的引用:这确实有效。但以下得到原始错误:
我猜测当没有本地引用时,整个查询被运送到远程系统以执行,并且由于某种原因
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 捕获了任何异常或 OLEDB 事件:
事件:
列过滤器:
测试
测试 1
CAST(NULL AS UNIQUEIDENTIFIER)
这样可行XML 执行计划的相关部分:
测试 2
CAST(NULL AS UNIQUEIDENTIFIER)
失败了(注意:我将子查询保留在其中,注释掉了,这样当我比较 XML 跟踪文件时差异就会减少)
测试 3
CAST(NULL AS DATE)
这样可行(注意:我将子查询保留在其中,注释掉了,这样当我比较 XML 跟踪文件时差异就会减少)
XML 执行计划的相关部分:
如果您查看测试#3,它正在
SELECT TOP (2) NULL
“远程”系统上进行。SQL Profiler 跟踪显示该远程字段的数据类型实际上是INT
。跟踪还显示,客户端(即我从中运行查询的位置)的字段是DATE
,正如预期的那样。INT
从to的转换DATE
,会在 SQL Server 中出现错误,在 OLEDB 驱动程序中工作得很好。远程值是NULL
,因此直接返回,因此<ColumnReference Column="Expr1002" />
.如果您查看测试#1,它正在
SELECT 1
“远程”系统上进行。SQL Profiler 跟踪显示该远程字段的数据类型实际上是INT
。跟踪还显示,客户端(即我从中运行查询的位置)的字段是GUID
,正如预期的那样。从INT
to的转换GUID
(请记住,这是在驱动程序中完成的,OLEDB 将其称为“GUID”),这会在 SQL Server 中出现错误,但在 OLEDB 驱动程序中工作得很好。远程值不是NULL
,因此它被替换为文字NULL
,因此<Const ConstValue="NULL" />
.测试#2 失败,因此没有执行计划。但是,它确实成功地查询了“远程”系统,但无法传回结果集。SQL Profiler 捕获的查询是:
这就是在测试#1 中执行的完全相同的查询,但在这里它失败了。还有其他细微差别,但我无法完全解释 OLEDB 通信。但是,远程字段仍显示为
INT
(wType = 3 = adInteger / 四字节有符号整数 / DBTYPE_I4),而“客户端”字段仍显示为GUID
(wType = 72 = adGUID / 全局唯一标识符 / DBTYPE_GUID)。OLE DB 文档没有多大帮助,因为GUID Data Type Conversions、DBDATE Data Type Conversions和I4 Data Type Conversions表明不支持从I4转换为GUID或DBDATE,但DATE
查询有效。三个测试的 Trace XML 文件位于 PasteBin 上。如果您想查看每个测试与其他测试的不同之处的详细信息,您可以将它们保存在本地,然后对它们进行“差异”。这些文件是:
人体工程学?
该怎么办?可能只是我在顶部提到的解决方法,因为 SQL Native Client --
SQLNCLI11
自 SQL Server 2012 起已弃用。关于 SQL Server Native Client 主题的大多数 MSDN 页面在最佳:欲了解更多信息,请参阅:
ODBC ??
我通过以下方式设置了一个 ODBC 链接服务器:
然后尝试:
并收到以下错误:
附言
由于它涉及在远程服务器和本地服务器之间传输 GUID,因此非 NULL 值通过特殊语法处理。我在运行时注意到 SQL Profiler 跟踪中的以下 OLE DB 事件信息
CAST(0x00 AS UNIQUEIDENTIFIER)
:聚苯乙烯
我还通过
OPENQUERY
以下查询进行了测试:即使没有本地对象引用,它也成功了。SQL Profiler 跟踪 XML 文件已在以下位置发布到 PasteBin:
NullGuidSuccessOPENQUERY.xml
XML 执行计划使用
NULL
常量显示它,与测试#1 中的相同。只有一个丑陋的解决方法 - 使用一些日期常量
'1900-01-01'
代替null
.导入后,您可以将列更新
1900-01-01
为 Null。这是一种 SQL 2012 功能/错误,根据这里。
编辑:根据下面的@a_horse_with_no_name 评论替换
1900-00-00
为有效日期。1900-01-01
该问题与数据类型转换有关(如评论中所述)。
考虑以下:
请注意,
NullColumn
是 类型int
。SQL Server 不喜欢将int
值转换为uniqueidentifier
. 此SELECT
语句将在数据类型转换时失败:虽然可以将此特定值 (NULL) 强制转换为 GUID,但 SQL Server 会根据数据类型转换引发错误,甚至在查看特定值之前。相反,您将需要执行多步骤
CAST
操作以将隐式更改为int
可以完全转换为的数据类型 -- 这uniqueidentifer
意味着先转换为varchar
然后转换为uniqueidentifier
:OP 可以最终决定这是否是一个合适的答案。
我没有“绝对”证明,但我“怀疑”问题源于 UniqueIdentifer 依赖于服务器这一事实,并且提供者可能难以确定从哪个服务器(本地或远程)获取此唯一标识符,即使它是无效的。这就是为什么在这种情况下您可能可以成功转换任何其他数据类型,但不是唯一标识符。像 UNIQUEIDENTIFIERS 和 DATETIMEOFFSET 这样依赖于“服务器”的数据类型会给你遇到的错误。
使用 OPENQUERY 而不是 4 部分名称有效。
解决方法:接受的答案似乎表明转换需要在本地进行,因为 OLEDB 驱动程序不支持它。
所以我认为一个简单的解决方法(至少在我的查询中,
uniqueidentifier
在递归 CTE 的基本情况下选择一个 null)是声明一个 null 变量: