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