表值参数(TVP)允许您将多行结构化数据传递给存储过程或参数化查询。
go-mssqldb 驱动程序通过 mssql.TVP 类型支持 TVP。
当你需要将强类型行集传递到存储过程或命令中并保持列级结构时,可以使用 TVP。 对于进入目的表的最高吞吐量路径,可以使用 批量操作。 如果你的载荷是嵌套的,或者应用层已经是 JSON 格式,可以使用 JSON 和 XML 数据。
先决条件
创建一个用户自定义的表类型和一个接受该表的存储过程:
CREATE TYPE dbo.DepartmentType AS TABLE (
Name NVARCHAR(50),
GroupName NVARCHAR(50)
);
GO
CREATE PROCEDURE dbo.InsertDepartments
@departments dbo.DepartmentType READONLY
AS
BEGIN
INSERT INTO HumanResources.Department (Name, GroupName)
SELECT Name, GroupName FROM @departments;
END;
GO
为TVP行定义一个Go结构
创建一个与用户自定义表类型中列匹配的 Go 结构体类型:
type DepartmentRow struct {
Name string
GroupName string
}
将 TVP 传递给存储过程
创建一个包含类型名称和结构体切片的 mssql.TVP 值,然后将其作为参数传递:
import (
"context"
"database/sql"
"github.com/microsoft/go-mssqldb"
)
func insertDepartments(ctx context.Context, db *sql.DB, departments []DepartmentRow) error {
tvp := mssql.TVP{
TypeName: "dbo.DepartmentType",
Value: departments,
}
_, err := db.ExecContext(ctx, "dbo.InsertDepartments",
sql.Named("departments", tvp))
return err
}
调用函数:
departments := []DepartmentRow{
{Name: "Data Science", GroupName: "Research and Development"},
{Name: "Cloud Ops", GroupName: "Information Technology"},
{Name: "Developer Relations", GroupName: "Sales and Marketing"},
}
err := insertDepartments(ctx, db, departments)
列与字段映射
驱动程序通过位置(而非名称)将 TVP 列映射到结构体字段。 第一个结构字段映射到表类型的第一列,第二个字段映射到第二列,第三个字段映射到第三列,依此类推适用于表类型中的所有列。
要跳过某一列时,不能使用结构体标签。 重新排序或重构你的 Go 类型,使其与用户自定义表格类型的列顺序保持一致。
支持的字段类型
TVP 结构字段支持与常规参数相同的 Go 类型:
| 围棋场类型 | SQL Server 列类型 |
|---|---|
string |
nvarchar |
mssql.VarChar |
varchar |
int64、int32、int16、int8 |
bigint、int、smallint、tinyint |
float64、float32 |
float、real |
bool |
bit |
time.Time |
datetimeoffset |
[]byte |
varbinary |
mssql.UniqueIdentifier |
uniqueidentifier |
空的 TVP
你可以传一个空的切片。 存储过程接收一个包含零行的表:
tvp := mssql.TVP{
TypeName: "dbo.DepartmentType",
Value: []DepartmentRow{},
}
模式限定类型名称
TypeName 必须是用户自定义的表类型名称(而非目标表名称或存储过程名称)。 如果表类型属于非默认模式,则将该模式包含在 TypeName:
tvp := mssql.TVP{
TypeName: "HumanResources.DepartmentType",
Value: departments,
}