Migration von anderen Treibern zu go-mssqldb

Dieser Leitfaden hilft Go-Entwicklern dabei, von PostgreSQL, MySQL und anderen Datenbanktreibern auf SQL Server zu go-mssqldb migrieren. Es behandelt SQL-Syntaxunterschiede, treiberspezifische Änderungen und entsprechende Muster für gängige Operationen.

Hinweis

Beispiele in diesem Artikel laufen gegen die AdventureWorks2025-Beispieldatenbank .

Warum mit go-mssqldb auf SQL Server migrieren

Funktion SQL Server mit go-mssqldb
Unternehmenssicherheit Immer verschlüsselt, Zeilensicherheit, dynamische Datenmaskierung, transparente Datenverschlüsselung (TDE).
Authentication Microsoft Entra ID (früher Azure AD), verwaltete Identität, Windows-Authentifizierung, Kerberos.
Azure-Integration Native Unterstützung der Azure SQL-Datenbank mit automatischer Verschlüsselung, passwortlosen Verbindungen und Failover-Handling.
Leistung Spaltenspeicher-Indexe, In-Memory OLTP, Abfragespeicher für automatische Plankorrektur.
Datenformate Eingebaute JSON- und XML-Unterstützung mit serverseitiger Indizierung und Abfragen.
Tooling SQL Server Management Studio (SSMS), Azure Data Studio, Visual Studio Code SQL-Erweiterungen.

Checkliste für Migrationsüberlegungen

Überprüfen Sie diese Punkte, bevor Sie mit der Portierung von Abfragen beginnen, Zeile für Zeile:

Consideration Was zu ändern ist
Die Platzhaltersyntax unterscheidet sich von vielen Go-Treibern. Ersetzen Sie ?, $1 und ähnliche Platzhalter durch @name oder @p1, und übergeben Sie dann gegebenenfalls sql.Named()-Argumente.
LastInsertId() wird nicht unterstützt. Verwenden Sie stattdessen OUTPUT INSERTED.<column> oder SELECT SCOPE_IDENTITY().
Go-Werte string werden standardmäßig Unicode-Typen zugeordnet. Standardmäßig wird die Semantik von nvarchar verwendet, sofern Sie nicht für Nicht-Unicode-Daten einen treiberspezifischen Typ wie mssql.VarChar wählen.
Temporäre Tabellen sind auf die physische Verbindung beschränkt. Bewahre die Create-and-Use-Temp-Table-Logik auf derselben Verbindung oder innerhalb derselben Transaktion, falls spätere Anweisungen von dieser Tabelle abhängen.
Die Microsoft Entra-Authentifizierung verwendet eine andere Treiberregistrierung. Importieren Sie github.com/microsoft/go-mssqldb/azuread, verwenden Sie sql.Open("azuresql", ...), und legen Sie die Azure SQL-TLS-Optionen mit encrypt=true&TrustServerCertificate=false explizit fest.

Für ausführlichere Hinweise zu diesen Bereichen siehe Abfragen und Statements, Einschränkungen, Datentyp-Mappings, Microsoft Entra ID-Authentifizierung und Fehlerbehebung.

Von lib/pq (PostgreSQL) migrieren

Ändern Sie den Import und den Treibernamen

Ersetzen Sie den Import und den lib/pq Treibernamen durch 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)

Ändern Sie das Format des Verbindungszeichenfolge

Konvertiere das PostgreSQL-Schlüssel-Wert-Format in eine SQL Server-URL:

// 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"

Parameteränderungs-Platzhalter

PostgreSQL verwendet $1, $2 positionsbezogene Platzhalter. SQL Server verwendet benannte Parameter mit @:

// 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))

Änderungen der SQL-Syntax

Operation PostgreSQL SQL Server
Automatisch inkrementieren SERIAL oder GENERATED ALWAYS AS IDENTITY IDENTITY(1,1)
Setze einen Ausweis ein RETURNING id OUTPUT INSERTED.id oder SELECT SCOPE_IDENTITY()
Boolescher Typ BOOLEAN BIT
Zeichenfolgenverkettung \|\| + oder CONCAT()
Aktueller Zeitstempel NOW() oder CURRENT_TIMESTAMP GETUTCDATE() oder SYSDATETIMEOFFSET()
Zeilen begrenzen LIMIT 10 OFFSET 20 OFFSET 20 ROWS FETCH NEXT 10 ROWS ONLY
Zeichenfolgentyp TEXT oder VARCHAR NVARCHAR(MAX) oder NVARCHAR(n)
JSON-Extraktion column->>'key' JSON_VALUE(column, '$.key')
Upsert INSERT ... ON CONFLICT DO UPDATE MERGE-Anweisung
Array-Typ INTEGER[] Keine nativen Arrays. Verwenden Sie tabellenwertige Parameter.
Groß- und Kleinschreibung Standardmäßig groß- und kleinschreibungssensitiv Standardmäßig groß- und kleinschreibungsunsensitiv (hängt von der Sortierung ab).

Eingefügte ID abrufen

Ersetzen Sie die RETURNING Klausel von PostgreSQL durch die OUTPUT Klausel von 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)

Paginierung

Ersetzen Sie die Syntax LIMIT/OFFSET durch die Syntax 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"

Migration von pgx (PostgreSQL)

Der Treiber pgx verwendet seinen eigenen Verbindungspool und eine eigene API, die sich von database/sqlunterscheiden. Um zu migrieren, wechsel auf die Standard-Schnittstelle database/sql .

Wechsel vom PGX-Pool zu Datenbank/SQL

Ersetzen Sie die pgxpool API durch die Standard-Schnittstelle 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))

Ersetzen Sie pgx-spezifische Funktionen

PGX-Funktion go-mssqlDB-Äquivalent
pgx.CollectRows Manuell rows.Next() und Schleife rows.Scan() .
pgx.RowToStructByName Manuell rows.Scan in Strukturfelder wechseln.
Batch-Abfragen (pgx.Batch) Mehrere ExecContext Aufrufe oder gespeicherte Prozeduren.
COPY FROM für Masseneinfügung mssql.CopyIn für Masseneinfügung.
pgx.ConnConfig msdsn.Config oder URL-basierte Verbindungszeichenketten.
pgxpool.Pool sql.DB mit SetMaxOpenConns und SetMaxIdleConns.

Von go-sql-driver/mysql (MySQL) migrieren

Vertausche den MySQL-Import- und Treibernamen

Ersetzen Sie den MySQL-Import- und Treibernamen durch 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)

Konvertiere das MySQL DSN-Format

Konvertiere das MySQL-DSN-Format in eine SQL Server-URL.

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

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

Fragezeichen-Platzhalter ersetzen

MySQL verwendet ? positionsbezogene Platzhalter.

// 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))

Vergleich der Syntax von MySQL und SQL Server

Operation MySQL SQL Server
Automatisch inkrementieren AUTO_INCREMENT IDENTITY(1,1)
Setze einen Ausweis ein LAST_INSERT_ID() SCOPE_IDENTITY() oder OUTPUT INSERTED.id
Zeilen begrenzen LIMIT 10 OFFSET 20 OFFSET 20 ROWS FETCH NEXT 10 ROWS ONLY
Aktueller Zeitstempel NOW() GETUTCDATE()
Wenn null IFNULL(expr, default) ISNULL(expr, default) oder COALESCE(expr, default)
Zeichenfolgenlänge LENGTH(str) LEN(str)
Substring SUBSTRING(str, start, len) SUBSTRING(str, start, len) (identisch)
Datumsformat DATE_FORMAT(d, '%Y-%m-%d') FORMAT(d, 'yyyy-MM-dd') oder CONVERT(VARCHAR, d, 23)
Upsert INSERT ... ON DUPLICATE KEY UPDATE MERGE-Anweisung
Backtick-Zitate `column` [column]

Ersetzen Sie MySQL-spezifische Syntax

MySQL-Syntax SQL Server-Äquivalent
AUTO_INCREMENT IDENTITY(1,1)
LIMIT n OFFSET m OFFSET m ROWS FETCH NEXT n ROWS ONLY (erfordert ORDER BY)

AUTO_INCREMENT auf 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
);

LIMIT zum OFFSET/FETCH:

-- 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;

Allgemeine Migrationsmuster

Zuletzt eingefügte ID abrufen

Verwenden Sie die OUTPUT Klausel oder SCOPE_IDENTITY() um den Identitätswert der eingefügten Zeile abzurufen:

// 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

Bevorzuge die OUTPUT Klausel gegenüber SCOPE_IDENTITY(). Die Klausel OUTPUT funktioniert mit Batch-Inserts und hängt nicht von der Reihenfolge der Anweisungen ab.

Upsert (Einfügen oder Aktualisieren)

Die ON CONFLICT von PostgreSQL und die ON DUPLICATE KEY UPDATE von MySQL entsprechen den MERGE von SQL Server:

_, 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))

Masseneinfügung

Ersetzen Sie PostgreSQL COPY oder MySQL LOAD DATA durch 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()

Handle NULL-Werte

Alle Go-Datenbanktreiber handhaben NULL-Werte gleich, weil sie die Typen database/sql verwenden.

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")
}

Transaktionen

Alle Go-Datenbanktreiber verwenden dieselbe database/sql Transaktions-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()

Migrationscheckliste

Step Action
Installieren des Treibers go get github.com/microsoft/go-mssqldb
Importe ändern Ersetze den alten Treiberimport durch _ "github.com/microsoft/go-mssqldb".
Aktualisieren von Verbindungszeichenfolgen Wechsel zum SQL Server URL-Format: sqlserver://user:pass@host?database=db.
Parameterplatzhalter ersetzen Ändern Sie $1/$2 oder ? mit sql.Named in @name.
SQL-Syntax aktualisieren Ändere LIMIT/OFFSET, RETURNING, NOW(), , SERIALund andere datenbankspezifische SQL-Optionen.
Massenvorgänge ersetzen Ändern COPY oder LOAD DATA zu mssql.CopyIn.
Schema-DDL aktualisieren Wechsel SERIAL/AUTO_INCREMENT zu IDENTITY, TEXT zu NVARCHAR, BOOLEAN zu .BIT
Testen Sie alle Abfragen Führe deine Testsuite mit einer SQL Server-Instanz aus, um Syntaxunterschiede zu erkennen.
Konfigurieren der Verschlüsselung Füge encrypt=true hinzu (oder nutze die automatische Azure-Erkennung) für die Produktion.
Authentifizierung einrichten Konfigurieren Sie Microsoft Entra ID für Azure oder SQL Server-Authentifizierung für On-Premises.