go-mssqldb 中的表值参数

表值参数(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
int64int32int16int8 bigintintsmallinttinyint
float64float32 floatreal
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,
}