sql >> Base de Datos >  >> RDS >> Sqlserver

Pasar un tipo de tabla definido por el usuario entre la base de datos de SQL Server

Este es un duplicado de ¿Puede crear un UDT de CLR para permitir un tipo de tabla compartida entre bases de datos?

Básicamente, los tipos de tabla definidos por el usuario no se pueden compartir entre bases de datos. Los UDT basados ​​en CLR pueden compartirse entre bases de datos, pero solo si se han cumplido ciertas condiciones, como que el mismo ensamblaje se cargue en ambas bases de datos y algunas otras cosas (los detalles se encuentran en la pregunta duplicada mencionada anteriormente).

Para esta situación particular, hay una forma de pasar la información desde DB1 a DB2 , aunque no es una solución elegante. Para usar un tipo de tabla, el contexto de su base de datos actual debe ser la base de datos en la que existe el tipo de tabla. Esto se hace a través del USE declaración, pero eso solo se puede hacer en SQL dinámico si es necesario hacerlo dentro de un procedimiento almacenado.

USE [DB1];
GO

CREATE PROCEDURE [dbo].[selectData]
    @psCustomList CustomList READONLY
AS
BEGIN
    -- create a temp table as it can be referenced in dynamic SQL
    CREATE TABLE #TempCustomList
    (
        [ID] [INT],
        [Display] [NVARCHAR] (100)
    );

    INSERT INTO #TempCustomList (ID, Display)
        SELECT ID, Display FROM @psCustomList;

    EXEC('
        USE [DB2];

        DECLARE @VarCustomList CustomList;

        INSERT INTO @VarCustomList (ID, Display)
            SELECT ID, Display FROM #TempCustomList;

        EXEC dbo.selectMoreData @VarCustomList;
     ');
END

ACTUALIZAR

Usando sp_executesql , ya sea en un intento de evitar la tabla temporal local simplemente pasando el UDTT como un TVP, o simplemente como un medio para realizar una consulta parametrizada, en realidad no funciona (aunque ciertamente parece que debería). Es decir, lo siguiente:

USE [DB1];
GO
CREATE PROCEDURE dbo.CrossDatabaseTableTypeA
(
    @TheUDTT dbo.TestTable1 READONLY
)
AS
SET NOCOUNT ON;

EXEC sp_executesql N'
  USE [DB2];
  SELECT DB_NAME() AS [CurrentDB];

  DECLARE @TableTypeDB2 dbo.TestTable2;
  INSERT INTO @TableTypeDB2 ([Col1])
    SELECT tmp.[Col1]
    FROM   @TableTypeDB1 tmp;

  --EXEC dbo.CrossDatabaseTableTypeB @TableTypeDB2;
  ',
  N'@TableTypeDB1 dbo.TestTable1 READONLY',
  @TableTypeDB1 = @TheUDTT;
GO


DECLARE @tmp dbo.TestTable1;
INSERT INTO @tmp ([Col1]) VALUES (1), (3);
SELECT * FROM @tmp;

EXEC dbo.CrossDatabaseTableTypeA @TheUDTT = @tmp;

fallará en "@TableTypeDB2 tiene un tipo de datos no válido", aunque muestra correctamente que DB2 es la base de datos "actual". Tiene algo que ver con cómo sp_executesql determina tipos de datos variables ya que el error se refiere a @TableTypeDB2 como "variable #2", aunque se crea localmente y no como parámetro de entrada.

De hecho, sp_executesql generará un error si se declara una sola variable (a través del parámetro de entrada de la lista de parámetros a sp_executesql ), incluso si nunca se hace referencia a él, y mucho menos se utiliza. Es decir, el siguiente código se encontrará con el mismo error de no poder encontrar la definición de UDTT que ocurre con la consulta inmediatamente anterior:

USE [DB1];
GO
CREATE PROCEDURE dbo.CrossDatabaseTableTypeC
AS
SET NOCOUNT ON;

EXEC sp_executesql N'
  USE [DB2];
  SELECT DB_NAME() AS [CurrentDB];

  DECLARE @TableTypeDB2 dbo.TestTable2;
  ',
  N'@SomeVar INT',
  @SomeVar = 1;
GO

(Gracias a @Mark Sowul por mencionar que sp_executesql no funciona al pasar variables)

SIN EMBARGO, este problema se puede solucionar (bueno, siempre que no intente pasar el TVP para evitar la tabla temporal, 2 consultas anteriores) cambiando la base de datos de ejecución de sp_executesql para que el proceso sea local a la base de datos en la que existe el otro TVP. Una cosa buena sobre sp_executesql es que, a diferencia de EXEC , es un procedimiento almacenado y, además, un procedimiento almacenado del sistema, por lo que puede calificarse por completo. Hacer uso de este hecho permite sp_executesql funcione, lo que también significa que no hay necesidad de USE [DB2]; instrucción dentro del SQL dinámico. El siguiente código funciona:

USE [DB1];
GO
CREATE PROCEDURE dbo.CrossDatabaseTableTypeD
(
    @TheUDTT dbo.TestTable1 READONLY
)
AS
SET NOCOUNT ON;

-- create a temp table as it can be referenced in dynamic SQL
CREATE TABLE #TempList
(
    [ID] [INT]
);

INSERT INTO #TempList ([ID])
   SELECT [Col1] FROM @TheUDTT;

EXEC [DB2].[dbo].sp_executesql N'
  SELECT DB_NAME() AS [CurrentDB];

  DECLARE @TableTypeDB2 dbo.TestTable2;
  INSERT INTO @TableTypeDB2 ([Col1])
    SELECT tmp.[ID]
    FROM   #TempList tmp;

  EXEC dbo.CrossDatabaseTableTypeB @TableTypeDB2;
  ',
  N'@SomeVariable INT',
  @SomeVariable = 1111;
GO