Noções Básicas de Banco de Dados
10 exemplos para você começar com database/sql - 7 básicos e 3 intermediários.
Busque em todas as páginas da documentação
10 exemplos para você começar com database/sql - 7 básicos e 3 intermediários.
mkdir dbdemo && cd dbdemo && go mod init example.com/dbdemo.go get github.com/mattn/go-sqlite3.main.go (ou arquivos separados em um pacote) e execute com go run ..Open cria um pool; Ping verifica a conectividade.
package main
import (
"context"
"database/sql"
"fmt"
"time"
_ "github.com/mattn/go-sqlite3"
)
func main() {
db, err := sql.Open("sqlite3", ":memory:")
if err != nil {
panic(err)
}
defer db.Close()
ctx, cancel := context.WithTimeout(context.Background(), 2*time.Second)
defer cancel()
fmt.Println(db.PingContext(ctx))
}sql.Open ainda não garante uma conexão ativa.defer db.Close() para liberar recursos do pool no desligamento.PingContext na inicialização para falhar rapidamente em DSNs inválidos.Relacionado: Acesso a Dados em Go: database/sql como Base - por que os drivers são separados
Exec executa instruções sem iteração de linha.
package main
import (
"context"
"database/sql"
"fmt"
_ "github.com/mattn/go-sqlite3"
)
func main() {
db, _ := sql.Open("sqlite3", ":memory:")
defer db.Close()
ctx := context.Background()
_, err := db.ExecContext(ctx, `CREATE TABLE users (id INTEGER PRIMARY KEY, name TEXT)`)
if err != nil {
panic(err)
}
res, err := db.ExecContext(ctx, `INSERT INTO users (name) VALUES (?)`, "Ada")
if err != nil {
panic(err)
}
id, _ := res.LastInsertId()
fmt.Println("inserted id:", id)
}ExecContext retorna sql.Result com RowsAffected e LastInsertId quando suportado.? para SQLite, $1 para Postgres).Relacionado: Migrações com golang-migrate & goose - versionamento de esquema
Leituras de linha única usam QueryRow mais Scan.
package main
import (
"context"
"database/sql"
"fmt"
_ "github.com/mattn/go-sqlite3"
)
func main() {
db, _ := sql.Open("sqlite3", ":memory:")
defer db.Close()
ctx := context.Background()
_, _ = db.ExecContext(ctx, `CREATE TABLE users (id INTEGER PRIMARY KEY, name TEXT)`)
_, _ = db.ExecContext(ctx, `INSERT INTO users (name) VALUES ('Grace')`)
var name string
err := db.QueryRowContext(ctx, `SELECT name FROM users WHERE id = 1`).Scan(&name)
fmt.Println(name, err)
}Scan devem corresponder às colunas selecionadas.sql.ErrNoRows significa zero linhas, não uma falha de transporte.ErrNoRows para erros de domínio antes dos manipuladores HTTP.Relacionado: Transações, Declarações Preparadas & sql.Tx - fluxos de múltiplas declarações
Consultas de múltiplas linhas iteram até Next retornar false.
package main
import (
"context"
"database/sql"
"fmt"
_ "github.com/mattn/go-sqlite3"
)
func main() {
db, _ := sql.Open("sqlite3", ":memory:")
defer db.Close()
ctx := context.Background()
_, _ = db.ExecContext(ctx, `CREATE TABLE users (id INTEGER PRIMARY KEY, name TEXT)`)
for _, n := range []string{"Ada", "Grace", "Linus"} {
_, _ = db.ExecContext(ctx, `INSERT INTO users (name) VALUES (?)`, n)
}
rows, err := db.QueryContext(ctx, `SELECT name FROM users ORDER BY id`)
if err != nil {
panic(err)
}
defer rows.Close()
for rows.Next() {
var name string
if err := rows.Scan(&name); err != nil {
panic(err)
}
fmt.Println(name)
}
fmt.Println(rows.Err())
}defer rows.Close() para liberar a conexão de volta para o pool.rows.Err() após o loop para erros de iteração.Relacionado: GORM vs sqlx vs Raw database/sql - auxiliares de scan
Use tipos sql.Null* para campos SQL anuláveis.
package main
import (
"context"
"database/sql"
"fmt"
_ "github.com/mattn/go-sqlite3"
)
func main() {
db, _ := sql.Open("sqlite3", ":memory:")
defer db.Close()
ctx := context.Background()
_, _ = db.ExecContext(ctx, `CREATE TABLE users (id INTEGER PRIMARY KEY, nickname TEXT)`)
_, _ = db.ExecContext(ctx, `INSERT INTO users (id) VALUES (1)`)
var nick sql.NullString
_ = db.QueryRowContext(ctx, `SELECT nickname FROM users WHERE id = 1`).Scan(&nick)
if nick.Valid {
fmt.Println(nick.String)
} else {
fmt.Println("nickname is NULL")
}
}Valid é falso quando a coluna é SQL NULL.*string) em structs de domínio quando NULL é comum.Relacionado: Melhores Práticas de Banco de Dados e Acesso a Dados - convenções de mapeamento
Prazos cancelam o trabalho quando os clientes saem.
package main
import (
"context"
"database/sql"
"fmt"
"time"
_ "github.com/mattn/go-sqlite3"
)
func main() {
db, _ := sql.Open("sqlite3", ":memory:")
defer db.Close()
ctx, cancel := context.WithTimeout(context.Background(), 50*time.Millisecond)
defer cancel()
_, err := db.ExecContext(ctx, `SELECT 1`)
fmt.Println(err)
}*Context em todos os caminhos de escopo de solicitação.statement_timeout do SQL quando os drivers demoram para cancelar.ctx.Err() quando o cancelamento causou a falha.Relacionado: Pooling de Conexão e Timeouts do database/sql - pool mais prazos
BeginTx agrupa instruções atomicamente.
package main
import (
"context"
"database/sql"
"fmt"
_ "github.com/mattn/go-sqlite3"
)
func main() {
db, _ := sql.Open("sqlite3", ":memory:")
defer db.Close()
ctx := context.Background()
_, _ = db.ExecContext(ctx, `CREATE TABLE accounts (id INTEGER PRIMARY KEY, balance INTEGER)`)
_, _ = db.ExecContext(ctx, `INSERT INTO accounts (id, balance) VALUES (1, 100), (2, 0)`)
tx, err := db.BeginTx(ctx, nil)
if err != nil {
panic(err)
}
_, err = tx.ExecContext(ctx, `UPDATE accounts SET balance = balance - 10 WHERE id = 1`)
if err != nil {
_ = tx.Rollback()
panic(err)
}
_, err = tx.ExecContext(ctx, `UPDATE accounts SET balance = balance + 10 WHERE id = 2`)
if err != nil {
_ = tx.Rollback()
panic(err)
}
fmt.Println(tx.Commit())
}ctx para BeginTx e as instruções dentro da tx.Relacionado: Transações, Declarações Preparadas & sql.Tx - padrões de rollback
PrepareContext amortiza o custo de análise para consultas repetidas.
package main
import (
"context"
"database/sql"
"fmt"
_ "github.com/mattn/go-sqlite3"
)
func main() {
db, _ := sql.Open("sqlite3", ":memory:")
defer db.Close()
ctx := context.Background()
_, _ = db.ExecContext(ctx, `CREATE TABLE users (id INTEGER PRIMARY KEY, name TEXT)`)
stmt, err := db.PrepareContext(ctx, `INSERT INTO users (name) VALUES (?)`)
if err != nil {
panic(err)
}
defer stmt.Close()
for _, name := range []string{"a", "b", "c"} {
if _, err := stmt.ExecContext(ctx, name); err != nil {
panic(err)
}
}
var count int
_ = db.QueryRowContext(ctx, `SELECT COUNT(*) FROM users`).Scan(&count)
fmt.Println(count)
}Prepare manual.Tx.Stmt para reassociar uma declaração preparada a uma transação.Relacionado: Transações, Declarações Preparadas & sql.Tx - Stmt em Tx
Construa listas de placeholders com segurança para filtros de comprimento variável.
package main
import (
"context"
"database/sql"
"fmt"
"strings"
_ "github.com/mattn/go-sqlite3"
)
func namesByIDs(ctx context.Context, db *sql.DB, ids []int64) ([]string, error) {
if len(ids) == 0 {
return nil, nil
}
placeholders := strings.TrimSuffix(strings.Repeat("?,", len(ids)), ",")
query := fmt.Sprintf(`SELECT name FROM users WHERE id IN (%s)`, placeholders)
args := make([]any, len(ids))
for i, id := range ids {
args[i] = id
}
rows, err := db.QueryContext(ctx, query, args...)
if err != nil {
return nil, err
}
defer rows.Close()
var names []string
for rows.Next() {
var n string
if err := rows.Scan(&n); err != nil {
return nil, err
}
names = append(names, n)
}
return names, rows.Err()
}
func main() {
db, _ := sql.Open("sqlite3", ":memory:")
defer db.Close()
ctx := context.Background()
_, _ = db.ExecContext(ctx, `CREATE TABLE users (id INTEGER PRIMARY KEY, name TEXT)`)
_, _ = db.ExecContext(ctx, `INSERT INTO users (id, name) VALUES (1,'Ada'), (2,'Grace')`)
fmt.Println(namesByIDs(ctx, db, []int64{1, 2}))
}Relacionado: GORM vs sqlx vs Raw database/sql - quando os auxiliares ajudam
Ajuste o pool para que as goroutines não esgotem as conexões do banco de dados.
package main
import (
"database/sql"
"fmt"
_ "github.com/mattn/go-sqlite3"
)
func main() {
db, _ := sql.Open("sqlite3", ":memory:")
defer db.Close()
db.SetMaxOpenConns(10)
db.SetMaxIdleConns(5)
db.SetConnMaxLifetime(0)
stats := db.Stats()
fmt.Printf("open=%d inUse=%d idle=%d\n",
stats.OpenConnections, stats.InUse, stats.Idle)
}MaxOpenConns deve estar alinhado com max_connections do banco de dados dividido pelas réplicas do aplicativo.db.Stats() para métricas para alertas de saturação.Relacionado: Pooling de Conexão e Timeouts do database/sql - dimensionamento de produção
Versões da Stack: Esta página foi escrita para Go 1.26.x (padrão GC Green Tea, go fix modernizers - verifique o patch na compilação), chi (mais recente - verifique na compilação), gin (mais recente - verifique na compilação), echo (mais recente - verifique na compilação), google.golang.org/grpc (mais recente - verifique na compilação), sigs.k8s.io/controller-runtime (mais recente - verifique na compilação), kubebuilder (mais recente - verifique na compilação), tinygo (mais recente - verifique os alvos de placa na compilação), wazero (mais recente - verifique na compilação) e golangci-lint (mais recente - verifique o conjunto de linters na compilação).
Revisado por Chris St. John·Última atualização: 19 de jul. de 2026