Миграция на go-mssqldb с других драйверов

Это руководство помогает разработчикам Go перейти с PostgreSQL, MySQL и других драйверов баз данных на go-mssqldb SQL Server. Он охватывает различия в синтаксисе SQL, специфичные для драйвера изменения и эквивалентные шаблоны для распространённых операций.

Note

Примеры в этой статье выполняются в образце базы данных AdventureWorks2025.

Зачем переходить на SQL Server с go-mssqldb

Функция SQL Server с go-mssqldb
Корпоративная безопасность Всегда шифровано, безопасность на уровне строк, динамическое маскирование данных, прозрачное шифрование данных (TDE).
Authentication Microsoft Entra ID (ранее Azure AD), управляемая идентичность, проверка подлинности Windows, Kerberos.
Интеграция с Azure Нативная поддержка База данных SQL Azure с автоматическим шифрованием, соединениями без пароля и обработкой резервного режима.
Performance Индексы Columnstore, OLTP в памяти, хранилище запросов для автоматической коррекции плана.
Форматы данных Встроенная поддержка JSON и XML с серверной индексацией и запросами.
Tooling SQL Server Management Studio (SSMS), Azure Data Studio, расширения SQL для Visual Studio Code.

Контрольный список по вопросам миграции

Ознакомьтесь с этими пунктами перед тем, как начинать портировать запросы строка за строкой:

Соображение Что изменить
Синтаксис плейсхолдеров отличается от синтаксиса, используемого во многих драйверах Go. Замените ?, $1 и аналогичные заполнители на @name или @p1, затем при необходимости передавайте аргументы sql.Named().
LastInsertId() не поддерживается. Вместо этого используются типы OUTPUT INSERTED.<column> или SELECT SCOPE_IDENTITY().
По умолчанию значения Go string сопоставляются с типами Unicode. Предполагается семантика nvarchar, если только не выбран тип, зависящий от драйвера, например mssql.VarChar, для данных не в формате Unicode.
Временные таблицы ограничены физическим соединением. Выполняйте создание и использование временной таблицы в рамках того же соединения или в той же транзакции, если последующие инструкции зависят от этой таблицы.
Аутентификация Microsoft Entra использует другую регистрацию водителя. Импортируйте github.com/microsoft/go-mssqldb/azuread, используйте sql.Open("azuresql", ...) и явным образом задайте параметры TLS для Azure SQL с помощью encrypt=true&TrustServerCertificate=false.

Для более подробной информации по этим вопросам см. разделы Запросы и операторы, Ограничения, Сопоставления типов данных, Аутентификация Microsoft Entra ID и Устранение неполадок.

Миграция с lib/pq (PostgreSQL)

Измените имя импорта и драйвера

Замените lib/pq импорт и имя драйвера на go-mssqldb:

import _ "github.com/lib/pq"
db, err := sql.Open("postgres", connString)

// After (SQL Server with go-mssqldb):
import _ "github.com/microsoft/go-mssqldb"
db, err := sql.Open("sqlserver", connString)

Изменить формат строки подключения

Преобразуйте формат key-value PostgreSQL в URL SQL Server:

// lib/pq connection string:
"host=<server> port=5432 user=<user> password=<password> dbname=AdventureWorks2025 sslmode=require"

// go-mssqldb connection string:
"sqlserver://<user>:<password>@<server>:1433?database=AdventureWorks2025&encrypt=true"

Изменить заполнители параметров

PostgreSQL использует позиционные параметры $1, $2. SQL Server использует именованные параметры с @:

// PostgreSQL (lib/pq):
db.QueryContext(ctx, "SELECT * FROM users WHERE id = $1 AND status = $2", id, status)

// SQL Server (go-mssqldb):
db.QueryContext(ctx, "SELECT ProductID, Name FROM Production.Product WHERE ProductID = @id AND Color = @color",
    sql.Named("id", id),
    sql.Named("color", color))

Изменения в синтаксисе SQL

Операция PostgreSQL SQL Server
Автоматическое увеличение SERIAL или GENERATED ALWAYS AS IDENTITY IDENTITY(1,1)
Вставьте ID RETURNING id OUTPUT INSERTED.id или SELECT SCOPE_IDENTITY()
Тип Boolean BOOLEAN BIT
Объединение строк \|\| + или CONCAT()
Текущая метка времени NOW() или CURRENT_TIMESTAMP GETUTCDATE() или SYSDATETIMEOFFSET()
Ограничить строки LIMIT 10 OFFSET 20 OFFSET 20 ROWS FETCH NEXT 10 ROWS ONLY
Тип строки TEXT или VARCHAR NVARCHAR(MAX) или NVARCHAR(n)
Извлечение JSON column->>'key' JSON_VALUE(column, '$.key')
Upsert INSERT ... ON CONFLICT DO UPDATE заявление MERGE
Тип массива INTEGER[] Нет нативных массивов. Используйте параметры с табличными значениями.
Учет регистра По умолчанию чувствительный к регистру По умолчанию нечувствительна к регистру (зависит от колляции).

Получите вставленный ID

Замените клаузу RETURNING PostgreSQL на клаузу OUTPUT SQL Server:

var id int
err := db.QueryRowContext(ctx,
    "INSERT INTO users (name, email) VALUES ($1, $2) RETURNING id",
    name, email).Scan(&id)

// SQL Server (go-mssqldb) using OUTPUT:
var id int
err := db.QueryRowContext(ctx,
    "INSERT INTO HumanResources.Department (Name, GroupName) OUTPUT INSERTED.DepartmentID VALUES (@name, @groupName)",
    sql.Named("name", name),
    sql.Named("groupName", groupName)).Scan(&id)

Pagination

Замените LIMIT/OFFSET синтаксис на OFFSET/FETCH NEXT синтаксис.

// PostgreSQL:
"SELECT * FROM employees ORDER BY id LIMIT $1 OFFSET $2"

// SQL Server (requires ORDER BY):
"SELECT ProductID, Name, ListPrice FROM Production.Product ORDER BY ProductID OFFSET @offset ROWS FETCH NEXT @limit ROWS ONLY"

Миграция с pgx (PostgreSQL)

Драйвер использует собственный pgx пул соединений и API, отличающиеся от database/sql. Для миграции переключитесь на стандартный database/sql интерфейс.

Переход от пула pgx к базе данных/SQL

Замените pgxpool API на стандартный database/sql интерфейс.

// Before (pgx):
import "github.com/jackc/pgx/v5/pgxpool"
pool, err := pgxpool.New(ctx, "postgres://user:pass@<server>:5432/AdventureWorks2025")
defer pool.Close()
rows, err := pool.Query(ctx, "SELECT * FROM users WHERE id = $1", id)

// After (go-mssqldb with database/sql):
import (
    "database/sql"
    _ "github.com/microsoft/go-mssqldb"
)
db, err := sql.Open("sqlserver", "sqlserver://user:pass@<server>:1433?database=AdventureWorks2025")
defer db.Close()
rows, err := db.QueryContext(ctx,
    "SELECT ProductID, Name FROM Production.Product WHERE ProductID = @id", sql.Named("id", id))

Замена специфических для pgx функций

Функция PGX Эквивалент go-mssqldb
pgx.CollectRows Ручной цикл rows.Next() и rows.Scan().
pgx.RowToStructByName Ручной rows.Scan ввод в поля структуры.
Пакетные запросы (pgx.Batch) Несколько вызовов ExecContext или хранимых процедур.
COPY FROM для массовой вставки mssql.CopyIn для пакетной вставки.
pgx.ConnConfig msdsn.Config или строки соединения на основе URL.
pgxpool.Pool sql.DB с SetMaxOpenConns и SetMaxIdleConns.

Переход с go-sql-driver/mysql (MySQL)

Поменяйте местами имена импорта MySQL и драйвера

Замените имя импорта и драйвера MySQL на go-mssqldb.

// Before (MySQL):
import _ "github.com/go-sql-driver/mysql"
db, err := sql.Open("mysql", connString)

// After (SQL Server):
import _ "github.com/microsoft/go-mssqldb"
db, err := sql.Open("sqlserver", connString)

Конвертировать формат MySQL DSN

Конвертируйте формат MySQL DSN в URL SQL Server.

// MySQL DSN:
"user:password@tcp(<server>:3306)/AdventureWorks2025?tls=true"

// go-mssqldb URL:
"sqlserver://user:password@<server>:1433?database=AdventureWorks2025&encrypt=true"

Заменить заполнители в виде вопросительных знаков

MySQL использует ? позиционные заполняющие элементы.

// MySQL:
db.QueryContext(ctx, "SELECT * FROM users WHERE id = ? AND status = ?", id, status)

// SQL Server:
db.QueryContext(ctx, "SELECT ProductID, Name FROM Production.Product WHERE ProductID = @id AND Color = @color",
    sql.Named("id", id),
    sql.Named("color", color))

Сравните синтаксис MySQL и SQL Server

Операция MySQL SQL Server
Автоматическое увеличение AUTO_INCREMENT IDENTITY(1,1)
Вставьте ID LAST_INSERT_ID() SCOPE_IDENTITY() или OUTPUT INSERTED.id
Ограничить строки LIMIT 10 OFFSET 20 OFFSET 20 ROWS FETCH NEXT 10 ROWS ONLY
Текущая метка времени NOW() GETUTCDATE()
Если null IFNULL(expr, default) ISNULL(expr, default) или COALESCE(expr, default)
Длина строки LENGTH(str) LEN(str)
Подстрока SUBSTRING(str, start, len) SUBSTRING(str, start, len) (то же самое)
Формат даты DATE_FORMAT(d, '%Y-%m-%d') FORMAT(d, 'yyyy-MM-dd') или CONVERT(VARCHAR, d, 23)
Upsert INSERT ... ON DUPLICATE KEY UPDATE заявление MERGE
Обратное цитирование `column` [column]

Заменить синтаксис, специфичный для MySQL,

Синтаксис MySQL Эквивалент SQL Server
AUTO_INCREMENT IDENTITY(1,1)
LIMIT n OFFSET m OFFSET m ROWS FETCH NEXT n ROWS ONLY (требуется ORDER BY)

AUTO_INCREMENT к IDENTITY:

-- MySQL:
CREATE TABLE users (
    id INT AUTO_INCREMENT PRIMARY KEY,
    name VARCHAR(100)
);

-- SQL Server:
CREATE TABLE HumanResources.NewDepartment (
    DepartmentID SMALLINT IDENTITY(1,1) PRIMARY KEY,
    Name NVARCHAR(50) NOT NULL
);

ОГРАНИЧЕНИЕ СМЕЩЕНИЯ/ПОЛУЧЕНИЯ:

-- MySQL:
SELECT * FROM users LIMIT 10 OFFSET 20;

-- SQL Server:
SELECT ProductID, Name, ListPrice
FROM Production.Product
ORDER BY ProductID
OFFSET 20 ROWS FETCH NEXT 10 ROWS ONLY;

Распространенные шаблоны миграции

Получите последний вставленный идентификатор

Используйте клаузу OUTPUT или SCOPE_IDENTITY() для извлечения значения идентичности вставленной строки:

// Pattern 1: OUTPUT clause (recommended, works with batch inserts).
var id int64
err := db.QueryRowContext(ctx,
    "INSERT INTO HumanResources.Department (Name, GroupName) OUTPUT INSERTED.DepartmentID VALUES (@name, @groupName)",
    sql.Named("name", "Engineering"),
    sql.Named("groupName", "Research and Development")).Scan(&id)

// Pattern 2: SCOPE_IDENTITY (works with single-row inserts).
var id int64
err := db.QueryRowContext(ctx,
    "INSERT INTO HumanResources.Department (Name, GroupName) VALUES (@name, @groupName); SELECT SCOPE_IDENTITY()",
    sql.Named("name", "Engineering"),
    sql.Named("groupName", "Research and Development")).Scan(&id)

Tip

Предпочитаю этот OUTPUT пункт вместо SCOPE_IDENTITY(). Предложение OUTPUT работает с пакетными вставками и не зависит от порядка инструкций.

Upsert (вставить или обновить)

Сопоставление PostgreSQL ON CONFLICT и MySQL ON DUPLICATE KEY UPDATE с SQL ServerMERGE:

_, err := db.ExecContext(ctx, `
    MERGE HumanResources.Department AS target
    USING (SELECT @id AS DepartmentID, @name AS Name, @groupName AS GroupName) AS source
    ON target.DepartmentID = source.DepartmentID
    WHEN MATCHED THEN
        UPDATE SET Name = source.Name, GroupName = source.GroupName, ModifiedDate = GETDATE()
    WHEN NOT MATCHED THEN
        INSERT (Name, GroupName, ModifiedDate) VALUES (source.Name, source.GroupName, GETDATE());`,
    sql.Named("id", dept.ID),
    sql.Named("name", dept.Name),
    sql.Named("groupName", dept.GroupName))

Массовая вставка

Замените PostgreSQL COPY или MySQL LOAD DATA на mssql.CopyIn.

import mssql "github.com/microsoft/go-mssqldb"

stmt, err := db.Prepare(mssql.CopyIn("HumanResources.Department", mssql.BulkOptions{}, "Name", "GroupName", "ModifiedDate"))
if err != nil {
    return err
}

for _, dept := range departments {
    _, err = stmt.Exec(dept.Name, dept.GroupName, time.Now())
    if err != nil {
        return err
    }
}

// Flush the buffer.
_, err = stmt.Exec()
if err != nil {
    return err
}
stmt.Close()

Обработка значений NULL

Все драйверы базы данных Go обрабатывают значения NULL одинаково, потому что используют типы database/sql .

var color sql.NullString
err := db.QueryRowContext(ctx,
    "SELECT Color FROM Production.Product WHERE ProductID = @id",
    sql.Named("id", 1)).Scan(&color)

if color.Valid {
    fmt.Println(color.String)
} else {
    fmt.Println("NULL")
}

Transactions

Все драйверы баз данных Go используют один и тот database/sql же API транзакций.

tx, err := db.BeginTx(ctx, nil)
if err != nil {
    return err
}
defer tx.Rollback()

_, err = tx.ExecContext(ctx,
    "UPDATE Production.ProductInventory SET Quantity = Quantity - @qty WHERE ProductID = @pid AND LocationID = @fromLoc",
    sql.Named("qty", qty),
    sql.Named("pid", productID),
    sql.Named("fromLoc", fromLocationID))
if err != nil {
    return err
}

_, err = tx.ExecContext(ctx,
    "UPDATE Production.ProductInventory SET Quantity = Quantity + @qty WHERE ProductID = @pid AND LocationID = @toLoc",
    sql.Named("qty", qty),
    sql.Named("pid", productID),
    sql.Named("toLoc", toLocationID))
if err != nil {
    return err
}

return tx.Commit()

Контрольный список миграции

Step Действие
Установка драйвера go get github.com/microsoft/go-mssqldb
Изменить импорты Замените старый импорт драйверов на _ "github.com/microsoft/go-mssqldb".
Обновление строк подключения Изменение формата URL SQL Server: sqlserver://user:pass@host?database=db.
Заменить заполнители параметров Измените $1/$2 или ? на @name с помощью sql.Named.
Обновление синтаксиса SQL Изменение LIMIT/OFFSET, RETURNING, NOW(), SERIAL и других видов SQL, специфичных для конкретных баз данных.
Заменить массовые операции Изменить COPY или LOAD DATA на mssql.CopyIn.
Обновление схемы DDL Изменить SERIAL/AUTO_INCREMENT на IDENTITY, TEXT на NVARCHAR, BOOLEAN на .BIT
Проверьте все запросы Запустите набор тестов в экземпляре SQL Server, чтобы выявить различия в синтаксисе.
Настройка шифрования Добавьте encrypt=true (или полагайтесь на автоматическое обнаружение Azure) для продакшена.
Настройка проверки подлинности Настройте Microsoft Entra ID для Azure или аутентификацию SQL Server для локальных систем.