gpt4 book ai didi

sql-server - 在 SQL Server 数据库之间传递用户定义的表类型

转载 作者:行者123 更新时间:2023-12-02 16:05:22 26 4
gpt4 key购买 nike

我在 SQL Server 的一个数据库中有一个用户定义的表类型(我们称之为 DB1)。

我的类型的定义非常简单,仅包含 2 列。创建我的类型的脚本如下:

CREATE TYPE [dbo].[CustomList] AS TABLE
(
[ID] [int] ,
[Display] [NVARCHAR] (100)
)

我还在另一个数据库上运行了相同的脚本,因此我的类型是在 2 个数据库上(我们将第二个数据库称为 DB2)。

现在,我从 C# 应用程序调用 DB1 中的存储过程,传入我的 CustomList 用户定义类型的参数。

DB1 中的过程现在需要调用传入此 CustomListDB2 上的过程。

因此,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。

提前致谢

最佳答案

这是 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 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

更新

使用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 在传递变量时不起作用)

但是,这个问题可以通过更改 的执行数据库来解决(好吧,只要您不尝试传递 TVP 以避免临时表 - 上面的 2 个查询) sp_executesql 以便该进程将位于另一个 TVP 所在的数据库的本地进程。 sp_executesql 的一个优点是,与 EXEC 不同,它是一个存储过程,而且是一个系统存储过程,因此它可以是完全限定的。利用这一事实可以使 sp_executesql 正常工作,这也意味着动态 SQL 中不需要 USE [DB2]; 语句。以下代码确实有效:

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

关于sql-server - 在 SQL Server 数据库之间传递用户定义的表类型,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/14504756/

26 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com