将 table 值参数传递给具有不同字段数的存储过程

Passing table valued parameter to stored proc with different number of fields

我有一个要求,我需要将 table 值参数传递给存储过程。客户端需要在不影响 C# 代码的情况下向 table 值类型添加字段的灵活性(即使它需要删除并重新创建所有相关存储过程)。是否有可能向 DataTable 添加的列少于数据类型中的列? 我们正在使用 Microsoft SQL Server 2008 R2。

我试过这个:

SQL:

CREATE TYPE [dbo].[MyDataType] As Table
(
    ID INT NULL,
    Name NVARCHAR(50) NULL
)

CREATE PROCEDURE [dbo].[MyProcedure]
(
    @myData As [dbo].[MyDataType] Readonly
)
AS
Begin
    Select * FROM @myData
End

C#:

DataTable myDataTable = new DataTable("MyDataType");
myDataTable.Columns.Add("Id", typeof(Int32));
myDataTable.Rows.Add(1);
myDataTable.Rows.Add(2);

SqlParameter parameter = new SqlParameter();
parameter.ParameterName = "@myData";
parameter.SqlDbType = System.Data.SqlDbType.Structured;
parameter.Value = myDataTable;
command.Parameters.Add(parameter); 

我遇到以下异常:

A first chance exception of type 'System.Data.SqlClient.SqlException' occurred in System.Data.dll
Additional information: Trying to pass a table-valued parameter with 1 column(s) where the corresponding user-defined table type requires 2 column(s).

您必须传递一个包含与数据库 table 列相同的列数的数据表。

DataTable myDataTable = new DataTable("MyDataType");
myDataTable.Columns.Add("ID", typeof(Int32));
myDataTable.Columns.Add("Name ", typeof(string));

第一步是通过查询您的 dataBase.INFORMATION_SCHEMA.

来执行查询以获取列的名称和类型
SELECT COLUMN_NAME,DATA_TYPE 
FROM yourDataBase.INFORMATION_SCHEMA.COLUMNS
WHERE  TABLE_NAME = 'yourTableName'

第二步是在循环语句中使用 This result 来构建您需要的数据表,您将把它作为参数发送

Foreach (var item in ColumnsNameType)
{
   myDataTable.Columns.Add(item.COLUMN_NAME, typeof(item.COLUMN_NAME));
}

我最终生成数据 table 动态如下:

   public static DataTable GetDataTableFromSchema(string userDefinedTableTypeName, SqlConnection connection)
    {
        var query = "SELECT SC.name, ST.name AS datatype FROM sys.columns SC " +
                    "INNER JOIN sys.types ST ON ST.system_type_id = SC.system_type_id AND ST.is_user_defined = 0 " +
                    "WHERE SC.object_id = " +
                    "   (SELECT type_table_object_id FROM sys.table_types WHERE name = '" + userDefinedTableTypeName + "');";

        var dataTable = new DataTable();

        using (var command = new SqlCommand(query, connection))
        {
            using (var sqlDataReader = command.ExecuteReader())
            {
                while (sqlDataReader.Read())
                {
                    var columnName = sqlDataReader["name"].ToString();
                    var sqlDbType = (SqlDbType) Enum.Parse(typeof (SqlDbType), sqlDataReader["datatype"].ToString(), true);
                    var clrType = GetClrType(sqlDbType);
                    dataTable.Columns.Add(columnName, clrType);
                }
            }                
        }


        return dataTable;
    }

首先获取数据的结构table(你的table类型)

DECLARE @ret yourTableName
SELECT * FROM @ret

然后在 C# 中用您的数据填充它并使用它来调用您的存储过程