Bases de datos
10 ejemplos para empezar con database/sql - 7 básicos y 3 intermedios.
Busca en todas las páginas de la documentación
10 ejemplos para empezar con database/sql - 7 básicos y 3 intermedios.
mkdir dbdemo && cd dbdemo && go mod init example.com/dbdemo.go get github.com/mattn/go-sqlite3.main.go (o archivos separados en un mismo paquete) y ejecútalo con go run ..Open crea un pool; Ping verifica la conectividad.
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 aún no garantiza una conexión activa.defer db.Close() para liberar los recursos del pool al apagar.PingContext al inicio para detectar rápidamente errores en DSNs incorrectos.Relacionado: Acceso a Datos en Go: database/sql como Base - por qué los drivers son separados
Exec ejecuta sentencias sin iteración de filas.
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("id insertado:", id)
}ExecContext devuelve sql.Result con RowsAffected y LastInsertId cuando son compatibles.? para SQLite, $1 para Postgres).Relacionado: Migraciones con golang-migrate & goose - versionado de esquemas
Las lecturas de una sola fila usan QueryRow más 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 deben coincidir con las columnas seleccionadas.sql.ErrNoRows significa cero filas, no un fallo de transporte.ErrNoRows a errores de dominio antes de los manejadores HTTP.Relacionado: Transacciones, Sentencias Preparadas y sql.Tx - flujos de múltiples sentencias
Las consultas de múltiples filas iteran hasta que Next devuelve falso.
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 devolver la conexión al pool.rows.Err() después del bucle para detectar errores de iteración.Relacionado: GORM vs sqlx vs Raw database/sql - ayudantes para escanear
Usa tipos sql.Null* para campos SQL anulables.
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 es NULL")
}
}Valid es falso cuando la columna es SQL NULL.*string) en las estructuras de dominio cuando NULL es común.Relacionado: Mejores Prácticas de Bases de Datos y Acceso a Datos - convenciones de mapeo
Los plazos cancelan el trabajo cuando los clientes se van.
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 en cada ruta con ámbito de solicitud.statement_timeout de SQL cuando los drivers tardan en cancelar.ctx.Err() cuando la cancelación causó el fallo.Relacionado: Pool de Conexiones y Tiempos de Espera de database/sql - pool más plazos
BeginTx agrupa sentencias atómicamente.
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 a BeginTx y a las sentencias dentro de la tx.Relacionado: Transacciones, Sentencias Preparadas y sql.Tx - patrones de rollback
PrepareContext amortiza el coste de parseo 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 re-enlazar una sentencia preparada a una transacción.Relacionado: Transacciones, Sentencias Preparadas y sql.Tx - Stmt en Tx
Construye listas de marcadores de posición de forma segura para filtros de longitud variable.
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}))
}unnest en Postgres.Relacionado: GORM vs sqlx vs Raw database/sql - cuándo ayudan los ayudantes
Ajusta el pool para que las goroutines no agoten las conexiones de la base de datos.
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("abiertas=%d enUso=%d ociosas=%d\n",
stats.OpenConnections, stats.InUse, stats.Idle)
}MaxOpenConns debe alinearse con max_connections de la base de datos dividido por las réplicas de la aplicación.db.Stats() a métricas para alertas de saturación.Relacionado: Pool de Conexiones y Tiempos de Espera de database/sql - dimensionamiento en producción
Versiones de Stack: Esta página fue escrita para Go 1.26.x (predeterminado GC de Green Tea, go fix modernizers - verifica el parche en la compilación), chi (última - verifica en la compilación), gin (última - verifica en la compilación), echo (última - verifica en la compilación), google.golang.org/grpc (última - verifica en la compilación), sigs.k8s.io/controller-runtime (última - verifica en la compilación), kubebuilder (última - verifica en la compilación), tinygo (última - verifica los objetivos de placa en la compilación), wazero (última - verifica en la compilación) y golangci-lint (última - verifica el conjunto de linters en la compilación).
Revisado por Chris St. John·Última actualización: 19 jul 2026