Catatan
Akses ke halaman ini memerlukan otorisasi. Anda dapat mencoba masuk atau mengubah direktori.
Akses ke halaman ini memerlukan otorisasi. Anda dapat mencoba mengubah direktori.
Panduan ini membantu pengembang Go bermigrasi dari PostgreSQL, MySQL, dan driver database lainnya ke go-mssqldb untuk SQL Server. Ini mencakup perbedaan sintaks SQL, perubahan khusus driver, dan pola yang setara untuk operasi umum.
Note
Contoh dalam artikel ini menggunakan basis data sampel AdventureWorks2025.
Mengapa bermigrasi ke SQL Server dengan go-mssqldb
| Feature | SQL Server dengan go-mssqldb |
|---|---|
| Keamanan perusahaan | Selalu Dienkripsi, keamanan tingkat baris, penyamaran data dinamis, enkripsi data transparan (TDE). |
| Authentication | Microsoft Entra ID (sebelumnya Azure AD), identitas terkelola, Windows authentication, Kerberos. |
| Integrasi Azure | Dukungan Azure SQL Database asli dengan enkripsi otomatis, koneksi tanpa kata sandi, dan penanganan failover. |
| Performance | Indeks penyimpan kolom, OLTP dalam memori, penyimpanan kueri untuk koreksi paket otomatis. |
| Format data | Dukungan JSON dan XML bawaan dengan pengindeksan dan kueri sisi server. |
| Tooling | SQL Server Management Studio (SSMS), Azure Data Studio, ekstensi SQL untuk Visual Studio Code. |
Daftar periksa pertimbangan migrasi
Tinjau item ini sebelum Anda mulai memindahkan kueri baris demi baris:
| Consideration | Apa yang harus diubah |
|---|---|
| Sintaks placeholder berbeda dari banyak driver Go. | Ganti ?, $1, dan placeholder serupa lainnya dengan @name atau @p1, lalu teruskan argumen sql.Named() jika perlu. |
LastInsertId() tidak didukung. |
Gunakan OUTPUT INSERTED.<column> atau SELECT SCOPE_IDENTITY() sebagai gantinya. |
Nilai Go string dipetakan ke tipe Unicode secara bawaan. |
Asumsikan semantik nvarchar, kecuali jika Anda memilih tipe khusus driver seperti mssql.VarChar untuk data non-Unicode. |
| Tabel sementara terbatas pada koneksi fisik. | Pastikan logika pembuatan dan penggunaan tabel sementara tetap berada pada koneksi yang sama atau dalam transaksi yang sama jika pernyataan berikutnya bergantung pada tabel tersebut. |
| Autentikasi Microsoft Entra menggunakan pendaftaran driver yang berbeda. | Imporgithub.com/microsoft/go-mssqldb/azuread, gunakansql.Open("azuresql", ...), dan atur opsi TLS Azure SQL secara eksplisit dengan encrypt=true&TrustServerCertificate=false. |
Untuk panduan yang lebih mendalam tentang area ini, lihat Kueri dan pernyataan, Batasan, Pemetaan jenis data, autentikasi Microsoft Entra ID, dan Pemecahan Masalah.
Bermigrasi dari lib/pq (PostgreSQL)
Mengubah nama impor dan driver
Ganti lib/pq nama impor dan driver dengan 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)
Mengubah format string koneksi
Mengonversi format nilai kunci PostgreSQL ke 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"
Mengubah placeholder parameter
PostgreSQL menggunakan placeholder posisional $1, $2. SQL Server menggunakan parameter bernama dengan @:
// 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))
Perubahan sintaks SQL
| Pengoperasian | PostgreSQL | SQL Server |
|---|---|---|
| Pertambahan otomatis |
SERIAL atau GENERATED ALWAYS AS IDENTITY |
IDENTITY(1,1) |
| Mendapatkan ID yang dimasukkan | RETURNING id |
OUTPUT INSERTED.id atau SELECT SCOPE_IDENTITY() |
| Jenis Boolean | BOOLEAN |
BIT |
| Penggabungan string | \|\| |
+ atau CONCAT() |
| Tanda waktu saat ini |
NOW() atau CURRENT_TIMESTAMP |
GETUTCDATE() atau SYSDATETIMEOFFSET() |
| Batasi jumlah baris | LIMIT 10 OFFSET 20 |
OFFSET 20 ROWS FETCH NEXT 10 ROWS ONLY |
| Jenis string |
TEXT atau VARCHAR |
NVARCHAR(MAX) atau NVARCHAR(n) |
| Ekstraksi JSON | column->>'key' |
JSON_VALUE(column, '$.key') |
| Upsert | INSERT ... ON CONFLICT DO UPDATE |
MERGE pernyataan |
| Jenis Array | INTEGER[] |
Tidak ada array bawaan. Gunakan parameter dengan nilai tabel. |
| Peka huruf besar/kecil | Peka huruf besar/kecil secara default | Tidak membedakan huruf besar/kecil secara bawaan (bergantung pada kolasi). |
Ambil ID yang dimasukkan
Ganti klausa PostgreSQL RETURNING dengan klausa SQL ServerOUTPUT:
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
Ganti LIMIT/OFFSET sintaks dengan OFFSET/FETCH NEXT sintaks.
// 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"
Bermigrasi dari pgx (PostgreSQL)
pgx Driver menggunakan kumpulan koneksi dan API sendiri yang berbeda dari database/sql. Untuk bermigrasi, beralih ke antarmuka standar database/sql .
Ubah dari kumpulan pgx ke database/sql
Ganti pgxpool API dengan antarmuka standar 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))
Ganti fitur khusus pgx
| Fitur PGX | padanan go-mssqldb |
|---|---|
pgx.CollectRows |
Manual rows.Next() dan rows.Scan() loop. |
pgx.RowToStructByName |
Secara manual rows.Scan ke field struct. |
Kueri batch (pgx.Batch) |
Beberapa ExecContext panggilan atau prosedur tersimpan. |
COPY FROM untuk sisipan massal |
mssql.CopyIn untuk sisipan massal. |
pgx.ConnConfig |
msdsn.Config atau string koneksi berbasis URL. |
pgxpool.Pool |
sql.DB dengan SetMaxOpenConns dan SetMaxIdleConns. |
Bermigrasi dari go-sql-driver/mysql (MySQL)
Tukar posisi impor MySQL dan nama driver
Ganti impor MySQL dan nama driver dengan 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)
Ubah format MySQL DSN
Konversi format MySQL DSN ke 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"
Ganti karakter pengganti tanda tanya
MySQL menggunakan ? placeholder posisional.
// 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))
Bandingkan sintaks MySQL dan SQL Server
| Pengoperasian | MySQL | SQL Server |
|---|---|---|
| Pertambahan otomatis | AUTO_INCREMENT |
IDENTITY(1,1) |
| Mendapatkan ID yang dimasukkan | LAST_INSERT_ID() |
SCOPE_IDENTITY() atau OUTPUT INSERTED.id |
| Batasi jumlah baris | LIMIT 10 OFFSET 20 |
OFFSET 20 ROWS FETCH NEXT 10 ROWS ONLY |
| Tanda waktu saat ini | NOW() |
GETUTCDATE() |
| Jika null | IFNULL(expr, default) |
ISNULL(expr, default) atau COALESCE(expr, default) |
| Panjang string | LENGTH(str) |
LEN(str) |
| Substring | SUBSTRING(str, start, len) |
SUBSTRING(str, start, len) (sama) |
| Format tanggal | DATE_FORMAT(d, '%Y-%m-%d') |
FORMAT(d, 'yyyy-MM-dd') atau CONVERT(VARCHAR, d, 23) |
| Upsert | INSERT ... ON DUPLICATE KEY UPDATE |
MERGE pernyataan |
| Penggunaan backtick sebagai tanda kutip | `column` |
[column] |
Ganti sintaks khusus MySQL
| Sintaks MySQL | Setara dengan SQL Server |
|---|---|
AUTO_INCREMENT |
IDENTITY(1,1) |
LIMIT n OFFSET m |
OFFSET m ROWS FETCH NEXT n ROWS ONLY(membutuhkan)ORDER BY |
AUTO_INCREMENT ke 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 ke 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;
Pola migrasi umum
Mendapatkan identitas terakhir yang disisipkan
Gunakan OUTPUT klausa atau SCOPE_IDENTITY() untuk mengambil nilai identitas baris yang disisipkan:
// 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
Lebih suka OUTPUT klausa daripada SCOPE_IDENTITY(). Klausa OUTPUT berfungsi dengan penyisipan batch dan tidak bergantung pada urutan pernyataan.
Upsert (sisipkan atau perbarui)
ON CONFLICT milik PostgreSQL dan ON DUPLICATE KEY UPDATE milik MySQL dipetakan ke MERGE milik 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))
Penyisipan massal
Ganti PostgreSQL COPY atau MySQL LOAD DATA dengan 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()
Menangani nilai NULL
Semua driver database Go menangani nilai NULL dengan cara yang sama karena mereka menggunakan jenisnya 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
Semua driver database Go menggunakan API transaksi yang sama database/sql .
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()
Daftar periksa migrasi
| Step | Action |
|---|---|
| Pasang driver | go get github.com/microsoft/go-mssqldb |
| Ubah impor | Ganti impor driver lama dengan _ "github.com/microsoft/go-mssqldb". |
| Memperbarui string koneksi | Ubah ke format URL SQL Server: sqlserver://user:pass@host?database=db. |
| Mengganti placeholder parameter | Ubah $1/$2 atau ? menjadi @name dengan .sql.Named |
| Perbarui sintaks SQL | Ubah LIMIT/OFFSET, RETURNING, , NOW(), SERIALdan SQL khusus database lainnya. |
| Mengganti operasi massal | Ubah COPY atau LOAD DATA menjadi mssql.CopyIn. |
| Perbarui skema DDL | Ubah SERIAL/AUTO_INCREMENT menjadi IDENTITY, TEXT menjadi , NVARCHAR menjadi .BOOLEANBIT |
| Uji semua kueri | Jalankan rangkaian pengujian Anda terhadap instans SQL Server untuk menangkap perbedaan sintaks. |
| Konfigurasikan enkripsi | Tambahkan encrypt=true (atau andalkan deteksi otomatis Azure) untuk produksi. |
| Mengonfigurasikan autentikasi | Konfigurasikan Microsoft Entra ID untuk Azure, atau autentikasi SQL Server untuk lokal. |