Hinweis
Für den Zugriff auf diese Seite ist eine Autorisierung erforderlich. Sie können versuchen, sich anzumelden oder das Verzeichnis zu wechseln.
Für den Zugriff auf diese Seite ist eine Autorisierung erforderlich. Sie können versuchen, das Verzeichnis zu wechseln.
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. |