이 가이드는 Go 개발자들이 PostgreSQL, MySQL 및 기타 데이터베이스 드라이버에서 SQL Server용으로 마이그레이션하는 go-mssqldb 데 도움을 줍니다. SQL 문법 차이, 드라이버별 변경 사항, 그리고 일반적인 연산에 적용되는 동등한 패턴을 다룹니다.
메모
이 글의 예시들은 AdventureWorks2025 샘플 데이터베이스를 기준으로 합니다.
왜 go-mssqldb와 함께 SQL Server로 마이그레이션해야 하나요?
| 특징 | go-mssqldb용 SQL Server |
|---|---|
| 엔터프라이즈 보안 | 항상 암호화, 행 수준 보안, 동적 데이터 마스킹, 투명 데이터 암호화(TDE). |
| Authentication | Microsoft Entra ID (구 Azure AD), 관리 식별, Windows 인증, Kerberos. |
| Azure 통합 | 자동 암호화, 비밀번호 없는 연결, 페일오버 처리가 포함된 네이티브 Azure SQL Database 지원. |
| Performance | 컬럼스토어 인덱스, 인메모리 OLTP, 자동 계획 수정을 위한 쿼리 스토어. |
| 데이터 형식 | 서버 측 인덱싱 및 쿼리가 포함된 내장 JSON 및 XML 지원. |
| Tooling | SQL Server Management Studio (SSMS), Azure Data Studio, Visual Studio Code SQL 확장 프로그램. |
이주 고려사항 체크리스트
쿼리를 한 줄 포팅하기 전에 다음 항목들을 검토하세요:
| 고려 사항 | 변경할 내용 |
|---|---|
| 자리 표시자 구문은 많은 Go 드라이버와 다릅니다. |
?, $1 및 이와 유사한 자리 표시자를 @name 또는 @p1로 바꾼 다음, 적절한 경우 sql.Named() 인수를 전달합니다. |
LastInsertId()는 지원되지 않습니다. |
대신 OUTPUT INSERTED.<column> 또는 SELECT SCOPE_IDENTITY()를 사용하십시오. |
Go string 값은 기본적으로 유니코드 타입에 매핑됩니다. |
비유니코드 데이터의 경우 mssql.VarChar와 같은 드라이버별 형식을 선택하지 않는 한 nvarchar 의미 체계를 예상하세요. |
| 임시 테이블은 물리적 연결 범위로 제한됩니다. | 이후 구문이 해당 테이블에 의존하는 경우, 임시 테이블을 생성하고 사용하는 로직은 동일한 연결에서 유지하거나 동일한 트랜잭션 내에 두세요. |
| Microsoft Entra 인증은 다른 드라이버 등록을 사용합니다. |
github.com/microsoft/go-mssqldb/azuread을(를) 가져오고, sql.Open("azuresql", ...)을(를) 사용하고, encrypt=true&TrustServerCertificate=false를 사용해 Azure SQL TLS 옵션을 명시적으로 설정하세요. |
이 영역에 대한 더 깊은 안내는 쿼리 및 문, 제한,데이터 타입 매핑, Microsoft Entra ID 인증, 문제 해결을 참조하세요.
lib/pq (PostgreSQL)에서 마이그레이션하기
가져오기 이름과 드라이버 이름을 변경하세요
lib/pq import 및 드라이버 이름을 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)
연결 문자열 형식을 변경하세요
PostgreSQL 키값 형식을 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"
매개변수 자리 표시자 변경
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 문법 변경
| Operation | PostgreSQL | SQL Server |
|---|---|---|
| 자동 증가 |
SERIAL 또는 GENERATED ALWAYS AS IDENTITY |
IDENTITY(1,1) |
| 삽입된 ID 가져오기 | RETURNING id |
OUTPUT INSERTED.id 또는 SELECT SCOPE_IDENTITY() |
| 불리언 자료형 | 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') |
| 업서트 | INSERT ... ON CONFLICT DO UPDATE |
MERGE 진술 |
| 배열 형식 | INTEGER[] |
네이티브 어레이는 없습니다. 테이블 값 매개변수를 사용하세요. |
| 대/소문자 구분 | 기본적으로 대소문자를 구분함 | 기본적으로 대소문자 구분이 안 됩니다(콜레이션에 따라 다릅니다). |
삽입된 신분증을 받아보세요
PostgreSQL의 절을 RETURNING SQL Server의 OUTPUT 절로 대체하세요:
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)
페이지 나누기
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 드라이버는 database/sql와는 다른 자체 연결 풀과 API를 사용합니다. 마이그레이션하려면 표준 database/sql 인터페이스로 전환하세요.
pgx 풀에서 데이터베이스/SQL로의 변경
API를 pgxpool 표준 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 |
매뉴얼로 Struct rows.Scan 필드를 조작하세요. |
배치 쿼리 (pgx.Batch) |
여러 ExecContext 호출이나 저장 프로시저. |
COPY FROM 대량 인서트용 |
mssql.CopyIn 대량 인서트용입니다. |
pgx.ConnConfig |
msdsn.Config 또는 URL 기반 연결 문자열을 사용했습니다. |
pgxpool.Pool |
SetMaxOpenConns 및 SetMaxIdleConns가 있는 sql.DB. |
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 형식을 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"
물음표 자리 표시자 교체
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 문법 비교
| Operation | 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 | SUBSTRING(str, start, len) |
SUBSTRING(str, start, len) (동일) |
| 날짜 형식 | DATE_FORMAT(d, '%Y-%m-%d') |
FORMAT(d, 'yyyy-MM-dd') 또는 CONVERT(VARCHAR, d, 23) |
| 업서트 | 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
);
OFFSET/FETCH에 대한 LIMIT:
-- 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;
일반적인 마이그레이션 패턴
마지막으로 삽입된 ID 값 가져오기
삽입된 행의 IDENTITY 값을 검색하려면 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
SCOPE_IDENTITY()보다 OUTPUT 조항을 선호하세요. 이 절은 OUTPUT 배치 삽입 항목에 적용되며 명세서 순서에 의존하지 않습니다.
업서트 (삽입 또는 업데이트)
PostgreSQL의 ON CONFLICT 및 MySQL의 ON DUPLICATE KEY UPDATE은 SQL Server의 MERGE에 매핑됩니다:
_, 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 데이터베이스 드라이버는 타입을 database/sql 사용하기 때문에 NULL 값을 동일하게 처리합니다.
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"교체하세요. |
| 연결 문자열 업데이트 | SQL Server URL 형식으로의 변경: sqlserver://user:pass@host?database=db. |
| 매개변수 자리 표시자 교체 |
sql.Named을 사용하여 $1/$2 또는 ?를 @name(으)로 변경하세요. |
| SQL 문법 업데이트 | 변경LIMIT/OFFSET, RETURNING, NOW(), SERIAL, 그리고 기타 데이터베이스별 SQL. |
| 대량 작업 바꾸기 |
COPY 또는 LOAD DATA을 mssql.CopyIn로 변경하세요. |
| 스키마 업데이트 DDL | 로 SERIAL, , /AUTO_INCREMENT, IDENTITY 로 변환.TEXTNVARCHARBOOLEANBIT |
| 모든 쿼리를 테스트하세요 | 테스트 스위트를 SQL Server 인스턴스와 비교해 문법 차이를 찾아보세요. |
| 암호화 구성 | 프로덕션 환경에서는 encrypt=true를 추가하거나 Azure 자동 감지를 사용하세요. |
| 인증 설정 | Azure용 Microsoft Entra ID 또는 온프레미스용 SQL Server 인증을 설정하세요. |