Примечание.
Для доступа к этой странице требуется авторизация. Вы можете попробовать войти или изменить каталоги.
Для доступа к этой странице требуется авторизация. Вы можете попробовать изменить каталоги.
Это руководство помогает разработчикам 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 для локальных систем. |