Saltar al contenido

¿Por qué esta transmisión explícita causa problemas solo con un servidor vinculado?

Solución:

Entonces, pude reproducir el error después de darme cuenta de que el CAST se estaba haciendo localmente, no en la instancia remota. Anteriormente había recomendado pasar a SP3 con la esperanza de solucionar este problema (en parte debido a que no puedo reproducir el error en SP3, y en parte debido a que es una buena idea de todos modos). Sin embargo, ahora que puedo reproducir el error, está claro que pasar al SP3, aunque probablemente sea una buena idea, no solucionará este problema. Y también reproduje el error en SQL Server 2008 R2 RTM y 2014 SP1 (usando un servidor vinculado local “loop-back” en los tres casos).

Parece que este problema tiene que ver con dónde la consulta se está ejecutando, o al menos donde partes) de ella se están ejecutando. Digo esto porque pude obtener el CAST operación para que funcione, pero solo al incluir una referencia a un objeto de base de datos local:

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);

Eso realmente funciona. Pero lo siguiente obtiene el error original:

SELECT rmt.*, CAST(NULL AS UNIQUEIDENTIFIER) AS [GUID]
FROM [Local].[database_name].[dbo].[table_name] rmt
CROSS JOIN (VALUES (1)) tmp(dummy);

Supongo que cuando no hay referencias locales, toda la consulta se envía al sistema remoto para ser ejecutada, y por alguna razón NULLs no se puede convertir a UNIQUEIDENTIFIER, o tal vez el NULL el controlador OLE DB está traduciendo incorrectamente.


Según las pruebas que he realizado, esto parecería ser un error, pero no estoy seguro de si el error se encuentra en SQL Server o en el controlador OLEDB / Cliente nativo de SQL Server. Sin embargo, el error de conversión se produce dentro del controlador OLEDB, por lo que no es necesariamente un problema de conversión de INT para UNIQUEIDENTIFIER (una conversión que no está permitida en SQL Server) ya que el controlador no usa SQL Server para hacer conversiones (SQL Server tampoco permite convertir INT para DATE, sin embargo, el controlador OLEDB lo maneja con éxito, como se muestra en una de las pruebas).

Hice tres pruebas. Para los dos que tuvieron éxito, miré los planes de ejecución XML que muestran la consulta que se está ejecutando de forma remota. Para los tres, capturé cualquier excepción o evento OLEDB a través de SQL Profiler:

Eventos:

  • Errores y advertencias
    • Atención
    • Excepción
    • Advertencias de ejecución
    • Mensaje de error del usuario
  • OLEDB
    • todos
  • TSQL
    • todos excepto:
      • SQL: StmtRecompile
      • Tipo estático de XQuery

Filtros de columna:

  • Nombre de la aplicación
    • DIFERENTE A % Intellisense%
  • SPID
    • Mayor que o igual 50

LOS EXÁMENES

  • Prueba 1

    • CAST(NULL AS UNIQUEIDENTIFIER) eso funciona
    SELECT TOP (2) CAST(NULL AS UNIQUEIDENTIFIER) AS [Something]
                 , (SELECT COUNT(*) FROM sys.[data_spaces]) AS [lcl]
    FROM [Local].[TEMPTEST].[sys].[objects] rmt;
    

    Parte relevante del plan de ejecución XML:

              
                
                
                  
                
              
      ...
    
    
  • Prueba 2

    • CAST(NULL AS UNIQUEIDENTIFIER) eso falla
    SELECT TOP (2) CAST(NULL AS UNIQUEIDENTIFIER) AS [Something]
             --  , (SELECT COUNT(*) FROM sys.[data_spaces]) AS [lcl]
    FROM [Local].[TEMPTEST].[sys].[objects] rmt;
    

    (nota: mantuve la subconsulta allí, comentada, por lo que sería una diferencia menos cuando comparé los archivos de seguimiento XML)

  • Prueba 3

    • CAST(NULL AS DATE) eso funciona
    SELECT TOP (2) CAST(NULL AS DATE) AS [Something]
             --  , (SELECT COUNT(*) FROM sys.[data_spaces]) AS [lcl]
    FROM [Local].[TEMPTEST].[sys].[objects] rmt;
    

    (nota: mantuve la subconsulta allí, comentada, por lo que sería una diferencia menos cuando comparé los archivos de seguimiento XML)

    Parte relevante del plan de ejecución XML:

              
                
                
                  
                    
                  
                
              
     ...
    
    

Si observa la Prueba n. ° 3, está haciendo un SELECT TOP (2) NULL en el sistema “remoto”. El seguimiento de SQL Profiler muestra que el tipo de datos de este campo remoto es de hecho INT. El seguimiento también muestra que el campo en el lado del cliente (es decir, desde donde estoy ejecutando la consulta) es DATE, como se esperaba. La conversión de INT para DATE, algo que obtendrá un error en SQL Server, funciona bien dentro del controlador OLEDB. El valor remoto es NULL, por lo que se devuelve directamente, de ahí el .

Si observa la Prueba n. ° 1, está haciendo un SELECT 1 en el sistema “remoto”. El seguimiento de SQL Profiler muestra que el tipo de datos de este campo remoto es de hecho INT. El seguimiento también muestra que el campo en el lado del cliente (es decir, desde donde estoy ejecutando la consulta) es GUID, como se esperaba. La conversión de INT para GUID (recuerde, esto se hace dentro del controlador, y OLEDB lo llama “GUID”), algo que obtendrá un error en SQL Server, funciona bien dentro del controlador OLEDB. El valor remoto es noNULL, por lo que se reemplaza con un literal NULL, por lo tanto, la .

La prueba n. ° 2 falla, por lo que no hay un plan de ejecución. Sin embargo, consulta el sistema “remoto” con éxito, pero no puede devolver el conjunto de resultados. La consulta que capturó SQL Profiler es:

SELECT TOP (2) NULL "Expr1002" FROM "TEMPTEST"."sys"."objects" "Tbl1001"

Esa es exactamente la misma consulta que se está haciendo en la Prueba n. ° 1, pero aquí está fallando. Hay otras diferencias menores, pero no puedo interpretar completamente la comunicación OLEDB. Sin embargo, el campo remoto todavía se muestra como INT (wType = 3 = adInteger / entero con signo de cuatro bytes / DBTYPE_I4) mientras que el campo “cliente” todavía se muestra como GUID (wType = 72 = adGUID / identificador único global / DBTYPE_GUID). La documentación de OLE DB no ayuda mucho, ya que las conversiones de tipos de datos GUID, las conversiones de tipos de datos DBDATE y las conversiones de tipos de datos I4 muestran que la conversión de I4 a cualquiera GUID o DBDATE no es compatible, sin embargo, el DATE consulta funciona.

Los archivos Trace XML para las tres pruebas se encuentran en PasteBin. Si desea ver los detalles de en qué se diferencia cada prueba de las demás, puede guardarlos localmente y luego hacer una “diferencia” en ellos. Los archivos son:

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

¿ES DECIR?

¿Qué hacer al respecto? Probablemente solo la solución que anoté en la sección superior, dado que SQL Native Client – SQLNCLI11 – está en desuso a partir de SQL Server 2012. La mayoría de las páginas de MSDN sobre el tema de SQL Server Native Client tienen el siguiente aviso en la parte superior:

Advertencia

SQL Server Native Client (SNAC) no es compatible más allá de SQL Server 2012. Evite el uso de SNAC en nuevos trabajos de desarrollo y planee modificar las aplicaciones que lo usan actualmente. El controlador ODBC de Microsoft para SQL Server proporciona conectividad nativa desde Windows a Microsoft SQL Server y Microsoft Azure SQL Database.

Para obtener más información, consulte:

  • Cliente nativo de SQL Server
  • Instalación de SQL Server Native Client

ODBC ??

Configuré un servidor vinculado ODBC a través de:

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;

Y luego probé:

SELECT CAST(NULL AS UNIQUEIDENTIFIER) AS [Something]
FROM [LocalODBC].[tempdb].[sys].[objects] rmt;

y recibió el siguiente error:

El proveedor OLE DB “MSDASQL” para el servidor vinculado “LocalODBC” devolvió el mensaje “No se admite la conversión solicitada”.
Msj 7341, nivel 16, estado 2, línea 53
No se puede obtener el valor de fila actual de la columna “(expresión generada por el usuario) .Expr1002” del proveedor OLE DB “MSDASQL” para el servidor vinculado “LocalODBC”.


PD

En lo que respecta al transporte de GUID entre servidores remotos y locales, los valores no NULL se manejan mediante una sintaxis especial. Noté la siguiente información del evento OLE DB en el seguimiento de SQL Profiler cuando ejecuté CAST(0x00 AS UNIQUEIDENTIFIER):


PPS

También probé a través de OPENQUERY con la siguiente consulta:

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;

y tuvo éxito, incluso sin la referencia del objeto local. El archivo XML de seguimiento de SQL Profiler se ha publicado en PasteBin en:

NullGuidSuccessOPENQUERY.xml

El plan de ejecución XML lo muestra usando un NULL constante, igual que en la Prueba # 1.

Solo hay una solución desagradable: use alguna constante de fecha como '1900-01-01' en lugar de null.

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

Después de la importación, puede actualizar las columnas con 1900-01-01 de nuevo a Null.

Esta es una especie de característica / error de SQL 2012 según aquí.

Editar: reemplazado 1900-00-00 con fecha válida 1900-01-01 según el comentario de @a_horse_with_no_name a continuación.

El problema está relacionado con las conversiones de tipos de datos (como se menciona en los comentarios).

Considera lo siguiente:

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

Tenga en cuenta que el NullColumn es de tipo int. A SQL Server no le gusta convertir int valores a uniqueidentifier. Esta SELECT declaración fallará en una conversión de tipo de datos:

--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;

Msg 529, nivel 16, estado 2, línea 3

No se permite la conversión explícita del tipo de datos int a uniqueidentifier.

Si bien este valor específico (NULL) se puede convertir en un GUID, SQL Server arroja el error en función de la conversión del tipo de datos, incluso antes de mirar los valores específicos. En su lugar, deberá realizar un CAST Operación para ir a cambiar lo implícito int a un tipo de datos que se puede convertir limpiamente en uniqueidentifer– lo que significa lanzar primero a varchar luego a 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;

Si entiendes que ha resultado de ayuda nuestro post, te agradeceríamos que lo compartas con el resto programadores de esta manera nos ayudas a dar difusión a esta información.

¡Haz clic para puntuar esta entrada!
(Votos: 0 Promedio: 0)



Utiliza Nuestro Buscador

Deja una respuesta

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *