这是一个重复的问题:Can you create a CLR UDT to allow for a shared Table type across databases?
简而言之,不能跨数据库共享用户定义的表类型。基于CLR的UDT可以在数据库之间共享,但前提条件是已经满足某些特定条件,如相同程序集被加载到两个数据库中,并且有一些其他细节(详见上述重复问题)。
对于这种情况,有一种方法可以将信息从DB1
传递到DB2
,但不是一种优雅的解决方案。要使用表类型,您当前的数据库上下文需要是表类型存在的数据库。这可以通过USE
语句完成,但如果需要在存储过程内进行,则只能通过动态SQL来完成。
USE [DB1];
GO
CREATE PROCEDURE [dbo].[selectData]
@psCustomList CustomList READONLY
AS
BEGIN
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
更新
使用 sp_executesql
,或者仅仅是作为执行参数化查询的一种方式,尝试通过将 UDTT 作为 TVP 直接传递来避免使用本地临时表,实际上并不起作用(尽管看起来应该会)。也就是说,以下内容:
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;
尽管正确显示DB2
为“当前”数据库,但将在“@TableTypeDB2具有无效数据类型”上失败。这与sp_executesql
如何确定变量数据类型有关,因为该错误将@TableTypeDB2
称为“变量#2”,即使它是本地创建的,而不是作为输入参数创建的。
事实上,即使从未引用或使用单个变量(通过参数列表输入参数传递给sp_executesql
),sp_executesql
也会出错。这意味着以下代码将遇到与上面立即查询发生的UDTT找不到定义的相同错误:
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
(感谢 @Mark Sowul 提醒,当传递变量时,sp_executesql
无法工作)
然而,这个问题可以通过改变 sp_executesql
的执行数据库来解决(只要您不是试图传入TVP以避免临时表-在上面的2个查询中)。sp_executesql
的一个好处是,与 EXEC
不同,它是一个存储过程,而且是一个系统存储过程,因此可以完全限定。利用这个特性可以让 sp_executesql
工作,这也意味着在动态 SQL 中不需要 USE [DB2];
语句。以下代码可以正常工作:
USE [DB1];
GO
CREATE PROCEDURE dbo.CrossDatabaseTableTypeD
(
@TheUDTT dbo.TestTable1 READONLY
)
AS
SET NOCOUNT ON;
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