Табличные параметры в go-mssqldb

Параметры с таблицьными значениями (TVP) позволяют передавать несколько строк структурированных данных в хранимую процедуру или параметризованный запрос. go-mssqldb драйвер поддерживает TVP с помощью типа mssql.TVP.

Используйте TVP, когда нужно передать сильно типизированный набор строк в сохранённую процедуру или команду, сохраняя структуру на уровне столбцов. Для пути с наибольшей пропускной способностью в таблицу назначения используйте Bulk operations. Если ваша полезная нагрузка является вложенной или уже представлена в формате JSON на уровне приложения, используйте данные JSON и XML.

Prerequisites

Создайте пользовательский тип таблицы и хранящую процедуру, которая его принимает:

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

Определите структуру Go для строк TVP

Создайте тип структуры 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 с полями структуры по позиции (а не по имени). Первое поле структуры соответствует первому столбцу в типе таблицы, второе поле соответствует второму столбцу, третье поле соответствует третьему столбцу и так далее для всех столбцов в типе таблицы.

Чтобы пропустить столбц, нельзя использовать теги struct. Измените порядок полей или структуру типа Go так, чтобы он соответствовал порядку столбцов в пользовательском табличном типе.

Поддерживаемые типы полей

Поля структуры TVP поддерживают те же типы Go, что и обычные параметры:

Тип поля 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,
}