表值参数使用需三步:先用create type定义用户自定义表类型(含架构前缀、结构严格匹配),再在存储过程中声明为readonly输入参数,最后通过同类型变量传入;ado.net中须设sqldbtype.structured及完整typename。

表值参数必须先定义用户自定义表类型,再在存储过程中声明为 READONLY 输入参数,最后在调用时用变量承载数据并传入——跳过任一环节都会报错。
必须先创建用户定义的表类型
SQL Server 不允许直接把临时表或 SELECT ... INTO 结果当参数传。你得用 CREATE TYPE 显式建一个类型,结构要和你要传的数据完全一致:
- 列名、顺序、数据类型、是否允许 NULL 都必须匹配
- 不能包含计算列、标识列、约束(如主键、外键、CHECK)
- 类型名需带架构前缀,例如
dbo.SalesOrderList
示例:
CREATE TYPE dbo.SalesOrderList AS TABLE (
OrderID INT,
ProductName NVARCHAR(100),
Quantity INT
);
存储过程里只能声明为 READONLY 输入参数
表值参数在过程体内不可修改,也不能用作 OUTPUT 参数——这是硬性限制,不是可选项。
- 声明语法必须是
@param_name dbo.YourTableType READONLY - 试图在过程里对它做
INSERT/UPDATE/DELETE会直接报错:Cannot modify a table-valued parameter - 不能加
OUTPUT关键字,否则语法错误:Incorrect syntax near 'OUTPUT'
正确写法:
CREATE PROCEDURE dbo.InsertOrders
@orders dbo.SalesOrderList READONLY
AS
BEGIN
INSERT INTO SalesOrder (OrderID, ProductName, Quantity)
SELECT OrderID, ProductName, Quantity FROM @orders;
END;
调用时必须用变量承载,不能直接传结果集
你不能写 EXEC dbo.InsertOrders (SELECT ...) 或 EXEC dbo.InsertOrders @temp_table——SQL Server 不支持这种传参方式。
- 必须先
DECLARE一个同类型的变量 - 用
INSERT INTO @var SELECT ...或逐行INSERT填充它 - 再把这个变量作为参数传入,且不能加
OUTPUT
示例:
DECLARE @orderList dbo.SalesOrderList; INSERT INTO @orderList VALUES (1001, N'Widget', 5), (1002, N'Gadget', 3); EXEC dbo.InsertOrders @orders = @orderList;
ADO.NET 中传 DataTable 要配对 SqlDbType.Structured
.NET 端最容易卡住的地方是类型没对齐:C# 的 DataTable 必须显式指定 SqlDbType.Structured,且 TypeName 必须和数据库中定义的表类型名(含架构)完全一致。
- 漏设
TypeName→ 报错:The table type parameter '@orders' must have a valid type name -
TypeName写成SalesOrderList(缺dbo.)→ 报错:Type not found - 列顺序或类型不匹配 → 运行时报类型转换失败
C# 片段关键点:
var param = cmd.Parameters.Add("@orders", SqlDbType.Structured);
param.TypeName = "dbo.SalesOrderList"; // 必须全名
param.Value = dataTable; // dataTable 列结构需与类型定义一致
表值参数本质是 tempdb 中的一张物理表,只是由 SQL Server 自动管理生命周期。很多人以为它是内存传递,其实数据已落盘——这解释了为什么它能高效处理上万行,也意味着并发场景下要注意 tempdb 压力。











