具有用户定义表类型的 SQL Server 存储过程

Posted

技术标签:

【中文标题】具有用户定义表类型的 SQL Server 存储过程【英文标题】:SQL Server stored procedure with user-defined table type 【发布时间】:2021-11-04 08:35:32 【问题描述】:

我在 SQL Server 中有一个带有参数 @Files 的存储过程,它是用户定义的表类型。从 C# 代码执行时出现错误:

System.Data.SqlClient.SqlException: '传入的表格数据流 (TDS) 远程过程调用 (RPC) 协议流不正确。表值参数 3 ("@Files"),第 0 行,第 0 列:数据类型 0xF3(用户定义的表类型)具有指定的非零长度数据库名称。数据库名称不允许与表值参数一起使用,只有模式名称和类型名称有效。'

这是我的 C# 代码:

DataTable dt = new DataTable();
dt.Columns.Add("ComputerId", typeof(int));
dt.Columns.Add("FolderId", typeof(int));
dt.Columns.Add("FilePath", typeof(string));
dt.Columns.Add("FileName", typeof(string));
dt.Columns.Add("Size", typeof(long));
dt.Columns.Add("LastWriteTimeUtc", typeof(DateTime));
dt.Columns.Add("ReadOnly", typeof(bool));

foreach (var item in listFileInformation)

    dt.Rows.Add(1, 1, item.FilePath, item.FileName, item.Size, item.LastWriteUTC, item.ReaddOnly);


databaseHelper.AddParameter("@ComputerId", 1);
databaseHelper.AddParameter("@FolderId", 1);

SqlParameter tableTypeParam = new SqlParameter("@Files", SqlDbType.Structured)
                                  
                                      TypeName = "Inbox.dbo.EngComputerFileTableType",
                                      Value = dt
                                  ;
databaseHelper.AddParameter(tableTypeParam);

databaseHelper.ExecuteNonQuery("InsertEngComputerFiles", System.Data.CommandType.StoredProcedure, System.Data.ConnectionState.Open);

databaseHelper.connection.Close();

这是存储过程:

CREATE PROCEDURE [dbo].[InsertEngComputerFiles]
    @ComputerId BIGINT,
    @FolderId BIGINT,
    @Files [dbo].[EngComputerFileTableType] READONLY
AS
BEGIN
    SET NOCOUNT ON;

    INSERT INTO
        [Inbox].[dbo].[EngComputerFile] (
            [ComputerId], [FolderId], [FilePath], [FileName],
            [Size], [LastWriteTimeUtc], [ReadOnly])
        SELECT
            @ComputerId, @FolderId, [FilePath], [FileName],
            [Size], [LastWriteTimeUtc], [ReadOnly]
        FROM
            @Files;

    UPDATE [Inbox].[dbo].[EngComputer]
    SET [LastScanTimeStampUtc] = SYSUTCDATETIME()
    WHERE [ComputerId] = @ComputerId;
END

【问题讨论】:

你为什么说TypeName = "Inbox.dbo.EngComputerFileTableType" 而不是TypeName = "dbo.EngComputerFileTableType"?该类型必须与您调用的存储过程在同一个数据库中才能正常工作,因此数据库前缀是不必要的(错误消息告诉您它不仅不必要而且无效)。 @AaronBertrand 感谢您的回复!但是我已经尝试过 dbo.EngComputerFileTableType 但它也给出了错误'Invalid object name 'Inbox.dbo.EngComputerFile'。' dbo.EngComputerFile dbo.EngComputerFileTableType @MiralShah . @Larnu 是真的!!! 不要在没有很好理由的情况下使用三部分名称。您的连接应确定数据库上下文。随着数据库在环境之间迁移,这些数据库的名称经常发生变化。使用 3 部分名称只会创建更多工作来支持这种非常常见的做法。 【参考方案1】:

消息说明一切 你需要改变这一行

SqlParameter tableTypeParam = new SqlParameter("@Files", SqlDbType.Structured)

//TypeName = "Inbox.dbo.EngComputerFileTableType",
TypeName = "dbo.EngComputerFileTableType",

Value = dt
;

【讨论】:

以上是关于具有用户定义表类型的 SQL Server 存储过程的主要内容,如果未能解决你的问题,请参考以下文章

使用 JDBC 将用户定义的表类型传递给 SQL Server 存储过程

SQL Server中用户定义的表类型的性能

SQL Server存储过程中使用表值作为输入参数示例

如何从 SQL Server 2014 中的 Select 查询中将数据分配给用户定义的表类型

SQL Server:关于用户定义的表类型的 ltrim

将用户定义表中的日期值传递给 SQL Server 2008 存储过程