我的类型的定义非常简单,只包含2列.创建我的类型的脚本如下:
CREATE TYPE [dbo].[CustomList] AS TABLE ( [ID] [int],[Display] [NVARCHAR] (100) )
我还在另一个数据库上运行相同的脚本,所以我的类型在2个数据库上(让我们调用第二个数据库DB2).
我现在从我的C#app调用DB1中的存储过程,传入我的CustomList用户定义类型的参数.
DB1中的过程现在需要调用DB2传递此CustomList的过程.
所以,DB1中的过程如下所示:
ALTER PROCEDURE [dbo].[selectData] @psCustomList CustomList ReadOnly AS BEGIN EXEC DB2.dbo.selectMoreData @psCustomList END
DB2中的过程就像这样(我只显示参数列表,因为这就是所需要的):
ALTER PROCEDURE [dbo].[selectMoreData] @psCustomList CustomList ReadOnly AS BEGIN ......
当我运行这个时,我收到以下错误:
Operand type clash: CustomList is incompatible with CustomList
任何人都有任何想法我做错了什么?
我正在使用sql Server 2008.
提前致谢
解决方法
实质上,用户定义的表类型不能跨数据库共享.基于CLR的UDT可以跨数据库共享,但仅在满足某些条件时才会共享,例如同一个程序集被加载到两个数据库中,以及其他一些事项(详细信息在上面提到的重复问题中).
对于这种特殊情况,有一种方法可以将信息从DB1传递给DB2,尽管它不是一个优雅的解决方案.要使用表类型,您当前的数据库上下文必须是表类型所在的数据库.这是通过USE语句完成的,但只有在需要在存储过程中完成时才能在动态sql中完成.
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
UPDATE
使用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;
将失败“@ TableTypeDB2具有无效的数据类型”,即使它正确显示DB2是“当前”数据库.它与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个查询)以便进程将是其他TVP所在的DB的本地.关于sp_executesql的一个好处是,与EXEC不同,它是一个存储过程,并且是一个系统存储过程,所以它可以是完全限定的.利用这个事实可以使sp_executesql工作,这也意味着不需要USE [DB2];动态sql中的语句.以下代码确实有效:
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