AskOverflow.Dev

AskOverflow.Dev Logo AskOverflow.Dev Logo

AskOverflow.Dev Navigation

  • 主页
  • 系统&网络
  • Ubuntu
  • Unix
  • DBA
  • Computer
  • Coding
  • LangChain

Mobile menu

Close
  • 主页
  • 系统&网络
    • 最新
    • 热门
    • 标签
  • Ubuntu
    • 最新
    • 热门
    • 标签
  • Unix
    • 最新
    • 标签
  • DBA
    • 最新
    • 标签
  • Computer
    • 最新
    • 标签
  • Coding
    • 最新
    • 标签
主页 / dba / 问题 / 147039
Accepted
krystah
krystah
Asked: 2016-08-17 23:42:58 +0800 CST2016-08-17 23:42:58 +0800 CST 2016-08-17 23:42:58 +0800 CST

为什么这种显式转换只会导致链接服务器出现问题?

  • 772

我正在通过源服务器上的视图从链接服务器查询数据。视图必须包含几个标准化的列,例如和Created,但在这种情况下,源服务器上的表没有任何合适的信息。因此,这些列被显式地转换为它们各自的类型。我更新了视图,将一列从ModifiedDeleted

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) o​​n 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) o​​n Windows NT 6.1 (Build 7601: Service Pack 1) (Hypervisor )

编辑
我刚刚意识到我犯了一个错误,没有发布所有有问题的专栏,我必须为遗漏一个重要细节而道歉。我不知道我怎么没有早点注意到这一点。不过,问题仍然存在。

转换为 DateTime 时不会发生错误转换,而是将列转换为 UniqueIdentifier。

这是罪魁祸首:

CAST(NULL AS UniqueIdentifier) AS [GUID]

SQL Server 2008 R2 支持 UniqueIdentifiers,正如评论中提到的,视图执行的查询在链接服务器上运行良好。

sql-server sql-server-2008-r2
  • 5 5 个回答
  • 6993 Views

5 个回答

  • Voted
  1. Best Answer
    Solomon Rutzky
    2016-08-24T06:17:45+08:002016-08-24T06:17:45+08:00

    CAST因此,在意识到这是在本地而不是在远程实例上完成之后,我能够重现该错误。我之前曾建议升级到 SP3,希望能解决这个问题(部分原因是无法在 SP3 上重现错误,部分原因是无论如何它都是一个好主意)。但是,现在我可以重现该错误,很明显升级到 SP3 虽然仍然可能是一个好主意,但并不能解决这个问题。而且我还重现了 SQL Server 2008 R2 RTM 和 2014 SP1 中的错误(在所有三种情况下都使用“环回”本地链接服务器)。

    似乎这个问题与查询的执行位置有关,或者至少与查询的部分执行位置有关。我这样说是因为我能够使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);
    

    我猜测当没有本地引用时,整个查询被运送到远程系统以执行,并且由于某种原因NULLs 无法转换为UNIQUEIDENTIFIER,或者NULLOLE DB 驱动程序可能错误地翻译了 。


    根据我所做的测试,这似乎是一个错误,但我不确定该错误是在 SQL Server 中还是在 SQL Server Native Client / OLEDB 驱动程序中。但是,转换错误发生在 OLEDB 驱动程序中,因此不一定是从转换INT到的问题UNIQUEIDENTIFIER(SQL Server 中不允许的转换),因为驱动程序没有使用 SQL Server 进行转换(SQL Server 也没有允许转换INT为DATE,但 OLEDB 驱动程序成功地处理了它,如测试之一所示)。

    我跑了三个测试。对于成功的两个,我查看了显示正在远程执行的查询的 XML 执行计划。对于这三个,我通过 SQL Profiler 捕获了任何异常或 OLEDB 事件:

    事件:

    • 错误和警告
      • 注意力
      • 例外
      • 执行警告
      • 用户错误信息
    • OLEDB
      • 全部
    • TSQL
      • 除了: _
        • SQL:Stmt重新编译
        • XQuery 静态类型

    列过滤器:

    • 应用名称
      • 不喜欢%Intellisense%
    • SPID
      • 大于或等于50

    测试

    • 测试 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 &quot;TEMPTEST&quot;.&quot;sys&quot;.&quot;objects&quot; &quot;Tbl1001&quot;"
       />
      
    • 测试 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 &quot;Expr1002&quot; FROM &quot;TEMPTEST&quot;.&quot;sys&quot;.&quot;objects&quot; &quot;Tbl1001&quot;" 
       />
      

    如果您查看测试#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,正如预期的那样。从INTto的转换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 Data Type Conversions、DBDATE Data Type Conversions和I4 Data Type Conversions表明不支持从I4转换为GUID或DBDATE,但DATE查询有效。

    三个测试的 Trace XML 文件位于 PasteBin 上。如果您想查看每个测试与其他测试的不同之处的详细信息,您可以将它们保存在本地,然后对它们进行“差异”。这些文件是:

    1. NullGuidSuccess.xml
    2. NullGuidError.xml
    3. NullDateSuccess.xml

    人体工程学?

    该怎么办?可能只是我在顶部提到的解决方法,因为 SQL Native Client --SQLNCLI11自 SQL Server 2012 起已弃用。关于 SQL Server Native Client 主题的大多数 MSDN 页面在最佳:

    警告

    SQL Server 2012 之后不支持 SQL Server Native Client (SNAC)。避免在新的开发工作中使用 SNAC,并计划修改当前使用它的应用程序。Microsoft ODBC Driver for SQL Server提供从 Windows 到 Microsoft SQL Server 和 Microsoft Azure SQL 数据库的本机连接。

    欲了解更多信息,请参阅:

    • SQL Server 本机客户端
    • 安装 SQL Server Native Client

    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 Profiler 跟踪中的以下 OLE DB 事件信息CAST(0x00 AS UNIQUEIDENTIFIER):

    <RemoteQuery RemoteSource="Local" RemoteQuery=
     "SELECT {guid'00000000-0000-0000-0000-000000000000'} &quot;Expr1002&quot; FROM &quot;TEMPTEST&quot;.&quot;sys&quot;.&quot;objects&quot; &quot;Tbl1001&quot;" 
     />
    

    聚苯乙烯

    我还通过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 中的相同。

    • 13
  2. Anton Krouglov
    2016-08-23T06:05:14+08:002016-08-23T06:05:14+08:00

    只有一个丑陋的解决方法 - 使用一些日期常量'1900-01-01'代替null.

    CAST('1900-01-01' as DateTime) as Modified
    

    导入后,您可以将列更新1900-01-01为 Null。

    这是一种 SQL 2012 功能/错误,根据这里。

    编辑:根据下面的@a_horse_with_no_name 评论替换1900-00-00为有效日期。1900-01-01

    • 4
  3. AMtwo
    2016-08-26T04:54:39+08:002016-08-26T04:54:39+08:00

    该问题与数据类型转换有关(如评论中所述)。

    考虑以下:

    SELECT NULL as NullColumn INTO SomeTable;
    EXEC sp_help SomeTable;
    DROP TABLE SomeTable;
    

    请注意,NullColumn是 类型int。SQL Server 不喜欢将int值转换为uniqueidentifier. 此SELECT语句将在数据类型转换时失败:

    --Just a SELECT from nothing
    SELECT CAST(CAST(NULL as int) as uniqueidentifier);
    --
    --or to see it from a physical table:
    SELECT NULL as NullColumn INTO SomeTable;
    SELECT CAST(NullColumn as uniqueidentifier) FROM SomeTable;
    DROP TABLE SomeTable;
    

    消息 529,第 16 层,状态 2,第 3 行

    不允许从数据类型 int 到 uniqueidentifier 的显式转换。

    虽然可以将此特定值 (NULL) 强制转换为 GUID,但 SQL Server 会根据数据类型转换引发错误,甚至在查看特定值之前。相反,您将需要执行多步骤CAST操作以将隐式更改为int可以完全转换为的数据类型 -- 这uniqueidentifer意味着先转换为varchar然后转换为uniqueidentifier:

    --Just a SELECT from nothing
    SELECT CAST(CAST(CAST(NULL as int) as varchar) as uniqueidentifier);
    --
    --or to see it from a physical table:
    SELECT NULL as NullColumn INTO SomeTable;
    SELECT CAST(CAST(NullColumn as varchar(32)) as uniqueidentifier) FROM SomeTable;
    DROP TABLE SomeTable;
    
    • 2
  4. Scott Hodgin - Retired
    2016-08-25T06:50:19+08:002016-08-25T06:50:19+08:00

    OP 可以最终决定这是否是一个合适的答案。

    我没有“绝对”证明,但我“怀疑”问题源于 UniqueIdentifer 依赖于服务器这一事实,并且提供者可能难以确定从哪个服务器(本地或远程)获取此唯一标识符,即使它是无效的。这就是为什么在这种情况下您可能可以成功转换任何其他数据类型,但不是唯一标识符。像 UNIQUEIDENTIFIERS 和 DATETIMEOFFSET 这样依赖于“服务器”的数据类型会给你遇到的错误。

    使用 OPENQUERY 而不是 4 部分名称有效。

    set nocount on  
    DECLARE @cmd nVARCHAR(max)
    DECLARE @datatype SYSNAME
    
    DECLARE _CURSOR CURSOR LOCAL FORWARD_ONLY STATIC READ_ONLY
    FOR
    SELECT NAME
    FROM sys.types 
    
    OPEN _CURSOR
    
    FETCH NEXT
    FROM _CURSOR
    INTO @datatype
    
    WHILE @@FETCH_STATUS = 0
    BEGIN
        BEGIN TRY
            SET @cmd = 'select top 1 cast(null as ' + @Datatype + ') as CastedData from remoteserver.remotedatabase.remoteschema.remotetable'
            PRINT @cmd
            EXECUTE sp_executesql @cmd
        END TRY
    
        BEGIN CATCH
            PRINT Error_message()
        END CATCH
    
    FETCH NEXT
    FROM _CURSOR
    INTO @datatype
    END --End While
    
    CLOSE _CURSOR
    
    DEALLOCATE _CURSOR
    
    • 1
  5. xr280xr
    2019-07-26T07:58:55+08:002019-07-26T07:58:55+08:00

    解决方法:接受的答案似乎表明转换需要在本地进行,因为 OLEDB 驱动程序不支持它。

    所以我认为一个简单的解决方法(至少在我的查询中,uniqueidentifier在递归 CTE 的基本情况下选择一个 null)是声明一个 null 变量:

    declare @nullGuid as uniqueidentifier = null;
    
    --Instead of...
    CAST(NULL AS UniqueIdentifier) AS [GUID]
    
    --use
    @nullGuid AS [GUID]
    
    • 0

相关问题

  • SQL Server - 使用聚集索引时如何存储数据页

  • 我需要为每种类型的查询使用单独的索引,还是一个多列索引可以工作?

  • 什么时候应该使用唯一约束而不是唯一索引?

  • 死锁的主要原因是什么,可以预防吗?

  • 如何确定是否需要或需要索引

Sidebar

Stats

  • 问题 205573
  • 回答 270741
  • 最佳答案 135370
  • 用户 68524
  • 热门
  • 回答
  • Marko Smith

    连接到 PostgreSQL 服务器:致命:主机没有 pg_hba.conf 条目

    • 12 个回答
  • Marko Smith

    如何让sqlplus的输出出现在一行中?

    • 3 个回答
  • Marko Smith

    选择具有最大日期或最晚日期的日期

    • 3 个回答
  • Marko Smith

    如何列出 PostgreSQL 中的所有模式?

    • 4 个回答
  • Marko Smith

    列出指定表的所有列

    • 5 个回答
  • Marko Smith

    如何在不修改我自己的 tnsnames.ora 的情况下使用 sqlplus 连接到位于另一台主机上的 Oracle 数据库

    • 4 个回答
  • Marko Smith

    你如何mysqldump特定的表?

    • 4 个回答
  • Marko Smith

    使用 psql 列出数据库权限

    • 10 个回答
  • Marko Smith

    如何从 PostgreSQL 中的选择查询中将值插入表中?

    • 4 个回答
  • Marko Smith

    如何使用 psql 列出所有数据库和表?

    • 7 个回答
  • Martin Hope
    Jin 连接到 PostgreSQL 服务器:致命:主机没有 pg_hba.conf 条目 2014-12-02 02:54:58 +0800 CST
  • Martin Hope
    Stéphane 如何列出 PostgreSQL 中的所有模式? 2013-04-16 11:19:16 +0800 CST
  • Martin Hope
    Mike Walsh 为什么事务日志不断增长或空间不足? 2012-12-05 18:11:22 +0800 CST
  • Martin Hope
    Stephane Rolland 列出指定表的所有列 2012-08-14 04:44:44 +0800 CST
  • Martin Hope
    haxney MySQL 能否合理地对数十亿行执行查询? 2012-07-03 11:36:13 +0800 CST
  • Martin Hope
    qazwsx 如何监控大型 .sql 文件的导入进度? 2012-05-03 08:54:41 +0800 CST
  • Martin Hope
    markdorison 你如何mysqldump特定的表? 2011-12-17 12:39:37 +0800 CST
  • Martin Hope
    Jonas 如何使用 psql 对 SQL 查询进行计时? 2011-06-04 02:22:54 +0800 CST
  • Martin Hope
    Jonas 如何从 PostgreSQL 中的选择查询中将值插入表中? 2011-05-28 00:33:05 +0800 CST
  • Martin Hope
    Jonas 如何使用 psql 列出所有数据库和表? 2011-02-18 00:45:49 +0800 CST

热门标签

sql-server mysql postgresql sql-server-2014 sql-server-2016 oracle sql-server-2008 database-design query-performance sql-server-2017

Explore

  • 主页
  • 问题
    • 最新
    • 热门
  • 标签
  • 帮助

Footer

AskOverflow.Dev

关于我们

  • 关于我们
  • 联系我们

Legal Stuff

  • Privacy Policy

Language

  • Pt
  • Server
  • Unix

© 2023 AskOverflow.DEV All Rights Reserve