Go Web 编程快速入门 18 - 附录B:查询与扫描

在 Go 语言的数据库操作中,查询和扫描是最常用的操作。本章将深入介绍如何使用 database/sql 包进行各种类型的查询操作,以及如何正确地扫描查询结果到 Go 的数据结构中。

1. 查询类型概述

1.1 查询方法分类

Go 的 database/sql 包提供了三种主要的查询方法:

  • Query: 执行返回多行结果的查询
  • QueryRow: 执行返回单行结果的查询
  • Exec: 执行不返回结果的语句(INSERT、UPDATE、DELETE)

1.2 基本查询示例

go 复制代码
package main

import (
    "database/sql"
    "fmt"
    "log"
    
    _ "github.com/lib/pq"
)

func basicQueryExamples(db *sql.DB) {
    // 1. Query - 多行查询
    rows, err := db.Query("SELECT id, name, email FROM users WHERE age > $1", 18)
    if err != nil {
        log.Fatal(err)
    }
    defer rows.Close()
    
    for rows.Next() {
        var id int
        var name, email string
        if err := rows.Scan(&id, &name, &email); err != nil {
            log.Fatal(err)
        }
        fmt.Printf("ID: %d, Name: %s, Email: %s\n", id, name, email)
    }
    
    // 2. QueryRow - 单行查询
    var userCount int
    err = db.QueryRow("SELECT COUNT(*) FROM users").Scan(&userCount)
    if err != nil {
        log.Fatal(err)
    }
    fmt.Printf("用户总数: %d\n", userCount)
    
    // 3. Exec - 执行语句
    result, err := db.Exec("UPDATE users SET last_login = NOW() WHERE id = $1", 1)
    if err != nil {
        log.Fatal(err)
    }
    
    rowsAffected, _ := result.RowsAffected()
    fmt.Printf("更新了 %d 行\n", rowsAffected)
}

2. 查询操作详解

2.1 多行查询(Query)

go 复制代码
package database

import (
    "database/sql"
    "fmt"
    "time"
)

// User 用户结构体
type User struct {
    ID        int       `json:"id"`
    Username  string    `json:"username"`
    Email     string    `json:"email"`
    Age       int       `json:"age"`
    CreatedAt time.Time `json:"created_at"`
    IsActive  bool      `json:"is_active"`
}

// QueryUsers 查询用户列表
func QueryUsers(db *sql.DB, minAge int, limit int) ([]*User, error) {
    query := `
        SELECT id, username, email, age, created_at, is_active 
        FROM users 
        WHERE age >= $1 
        ORDER BY created_at DESC 
        LIMIT $2`
    
    rows, err := db.Query(query, minAge, limit)
    if err != nil {
        return nil, fmt.Errorf("查询失败: %w", err)
    }
    defer rows.Close()
    
    var users []*User
    for rows.Next() {
        user := &User{}
        err := rows.Scan(
            &user.ID,
            &user.Username,
            &user.Email,
            &user.Age,
            &user.CreatedAt,
            &user.IsActive,
        )
        if err != nil {
            return nil, fmt.Errorf("扫描行失败: %w", err)
        }
        users = append(users, user)
    }
    
    // 检查迭代过程中的错误
    if err = rows.Err(); err != nil {
        return nil, fmt.Errorf("行迭代错误: %w", err)
    }
    
    return users, nil
}

// QueryUsersWithPagination 分页查询用户
func QueryUsersWithPagination(db *sql.DB, page, pageSize int) ([]*User, int, error) {
    // 计算偏移量
    offset := (page - 1) * pageSize
    
    // 查询总数
    var total int
    countQuery := "SELECT COUNT(*) FROM users"
    err := db.QueryRow(countQuery).Scan(&total)
    if err != nil {
        return nil, 0, fmt.Errorf("查询总数失败: %w", err)
    }
    
    // 查询分页数据
    query := `
        SELECT id, username, email, age, created_at, is_active 
        FROM users 
        ORDER BY id 
        LIMIT $1 OFFSET $2`
    
    rows, err := db.Query(query, pageSize, offset)
    if err != nil {
        return nil, 0, fmt.Errorf("查询失败: %w", err)
    }
    defer rows.Close()
    
    var users []*User
    for rows.Next() {
        user := &User{}
        err := rows.Scan(
            &user.ID,
            &user.Username,
            &user.Email,
            &user.Age,
            &user.CreatedAt,
            &user.IsActive,
        )
        if err != nil {
            return nil, 0, fmt.Errorf("扫描行失败: %w", err)
        }
        users = append(users, user)
    }
    
    if err = rows.Err(); err != nil {
        return nil, 0, fmt.Errorf("行迭代错误: %w", err)
    }
    
    return users, total, nil
}

2.2 单行查询(QueryRow)

go 复制代码
package database

import (
    "database/sql"
    "errors"
    "fmt"
)

// GetUserByID 根据ID获取用户
func GetUserByID(db *sql.DB, id int) (*User, error) {
    query := `
        SELECT id, username, email, age, created_at, is_active 
        FROM users 
        WHERE id = $1`
    
    user := &User{}
    err := db.QueryRow(query, id).Scan(
        &user.ID,
        &user.Username,
        &user.Email,
        &user.Age,
        &user.CreatedAt,
        &user.IsActive,
    )
    
    if err != nil {
        if errors.Is(err, sql.ErrNoRows) {
            return nil, fmt.Errorf("用户不存在: ID=%d", id)
        }
        return nil, fmt.Errorf("查询用户失败: %w", err)
    }
    
    return user, nil
}

// GetUserByEmail 根据邮箱获取用户
func GetUserByEmail(db *sql.DB, email string) (*User, error) {
    query := `
        SELECT id, username, email, age, created_at, is_active 
        FROM users 
        WHERE email = $1`
    
    user := &User{}
    err := db.QueryRow(query, email).Scan(
        &user.ID,
        &user.Username,
        &user.Email,
        &user.Age,
        &user.CreatedAt,
        &user.IsActive,
    )
    
    if err != nil {
        if errors.Is(err, sql.ErrNoRows) {
            return nil, fmt.Errorf("用户不存在: Email=%s", email)
        }
        return nil, fmt.Errorf("查询用户失败: %w", err)
    }
    
    return user, nil
}

// GetUserStats 获取用户统计信息
func GetUserStats(db *sql.DB) (map[string]interface{}, error) {
    query := `
        SELECT 
            COUNT(*) as total_users,
            COUNT(CASE WHEN is_active = true THEN 1 END) as active_users,
            COUNT(CASE WHEN is_active = false THEN 1 END) as inactive_users,
            AVG(age) as average_age,
            MIN(created_at) as first_user_date,
            MAX(created_at) as last_user_date
        FROM users`
    
    var totalUsers, activeUsers, inactiveUsers int
    var averageAge float64
    var firstUserDate, lastUserDate time.Time
    
    err := db.QueryRow(query).Scan(
        &totalUsers,
        &activeUsers,
        &inactiveUsers,
        &averageAge,
        &firstUserDate,
        &lastUserDate,
    )
    
    if err != nil {
        return nil, fmt.Errorf("查询统计信息失败: %w", err)
    }
    
    stats := map[string]interface{}{
        "total_users":     totalUsers,
        "active_users":    activeUsers,
        "inactive_users":  inactiveUsers,
        "average_age":     averageAge,
        "first_user_date": firstUserDate,
        "last_user_date":  lastUserDate,
    }
    
    return stats, nil
}

3. 扫描技术

3.1 基本扫描

go 复制代码
package database

import (
    "database/sql"
    "database/sql/driver"
    "fmt"
    "time"
)

// 基本类型扫描示例
func BasicScanExamples(db *sql.DB) {
    // 扫描到基本类型
    var id int
    var name string
    var age sql.NullInt64
    var email sql.NullString
    var createdAt time.Time
    
    query := "SELECT id, name, age, email, created_at FROM users WHERE id = $1"
    err := db.QueryRow(query, 1).Scan(&id, &name, &age, &email, &createdAt)
    if err != nil {
        fmt.Printf("扫描失败: %v\n", err)
        return
    }
    
    fmt.Printf("ID: %d\n", id)
    fmt.Printf("Name: %s\n", name)
    
    // 处理可空字段
    if age.Valid {
        fmt.Printf("Age: %d\n", age.Int64)
    } else {
        fmt.Println("Age: NULL")
    }
    
    if email.Valid {
        fmt.Printf("Email: %s\n", email.String)
    } else {
        fmt.Println("Email: NULL")
    }
    
    fmt.Printf("Created At: %v\n", createdAt)
}

3.2 可空类型处理

go 复制代码
package database

import (
    "database/sql"
    "database/sql/driver"
    "encoding/json"
    "fmt"
    "time"
)

// NullableUser 支持可空字段的用户结构体
type NullableUser struct {
    ID        int            `json:"id"`
    Username  string         `json:"username"`
    Email     sql.NullString `json:"email"`
    Age       sql.NullInt64  `json:"age"`
    Phone     sql.NullString `json:"phone"`
    Avatar    sql.NullString `json:"avatar"`
    IsActive  sql.NullBool   `json:"is_active"`
    CreatedAt time.Time      `json:"created_at"`
    UpdatedAt sql.NullTime   `json:"updated_at"`
}

// ToMap 转换为 map 格式,处理 NULL 值
func (u *NullableUser) ToMap() map[string]interface{} {
    result := map[string]interface{}{
        "id":         u.ID,
        "username":   u.Username,
        "created_at": u.CreatedAt,
    }
    
    if u.Email.Valid {
        result["email"] = u.Email.String
    } else {
        result["email"] = nil
    }
    
    if u.Age.Valid {
        result["age"] = u.Age.Int64
    } else {
        result["age"] = nil
    }
    
    if u.Phone.Valid {
        result["phone"] = u.Phone.String
    } else {
        result["phone"] = nil
    }
    
    if u.Avatar.Valid {
        result["avatar"] = u.Avatar.String
    } else {
        result["avatar"] = nil
    }
    
    if u.IsActive.Valid {
        result["is_active"] = u.IsActive.Bool
    } else {
        result["is_active"] = nil
    }
    
    if u.UpdatedAt.Valid {
        result["updated_at"] = u.UpdatedAt.Time
    } else {
        result["updated_at"] = nil
    }
    
    return result
}

// QueryNullableUser 查询支持可空字段的用户
func QueryNullableUser(db *sql.DB, id int) (*NullableUser, error) {
    query := `
        SELECT id, username, email, age, phone, avatar, is_active, created_at, updated_at
        FROM users 
        WHERE id = $1`
    
    user := &NullableUser{}
    err := db.QueryRow(query, id).Scan(
        &user.ID,
        &user.Username,
        &user.Email,
        &user.Age,
        &user.Phone,
        &user.Avatar,
        &user.IsActive,
        &user.CreatedAt,
        &user.UpdatedAt,
    )
    
    if err != nil {
        return nil, fmt.Errorf("查询用户失败: %w", err)
    }
    
    return user, nil
}

3.3 自定义扫描类型

go 复制代码
package database

import (
    "database/sql/driver"
    "encoding/json"
    "fmt"
    "strings"
)

// JSONMap 自定义 JSON 类型
type JSONMap map[string]interface{}

// Scan 实现 sql.Scanner 接口
func (j *JSONMap) Scan(value interface{}) error {
    if value == nil {
        *j = make(JSONMap)
        return nil
    }
    
    var bytes []byte
    switch v := value.(type) {
    case []byte:
        bytes = v
    case string:
        bytes = []byte(v)
    default:
        return fmt.Errorf("无法扫描 %T 到 JSONMap", value)
    }
    
    return json.Unmarshal(bytes, j)
}

// Value 实现 driver.Valuer 接口
func (j JSONMap) Value() (driver.Value, error) {
    if j == nil {
        return nil, nil
    }
    return json.Marshal(j)
}

// StringSlice 自定义字符串切片类型
type StringSlice []string

// Scan 实现 sql.Scanner 接口
func (s *StringSlice) Scan(value interface{}) error {
    if value == nil {
        *s = nil
        return nil
    }
    
    switch v := value.(type) {
    case []byte:
        *s = strings.Split(string(v), ",")
    case string:
        *s = strings.Split(v, ",")
    default:
        return fmt.Errorf("无法扫描 %T 到 StringSlice", value)
    }
    
    return nil
}

// Value 实现 driver.Valuer 接口
func (s StringSlice) Value() (driver.Value, error) {
    if s == nil {
        return nil, nil
    }
    return strings.Join(s, ","), nil
}

// UserProfile 用户配置文件
type UserProfile struct {
    ID       int         `json:"id"`
    Username string      `json:"username"`
    Settings JSONMap     `json:"settings"`
    Tags     StringSlice `json:"tags"`
}

// QueryUserProfile 查询用户配置文件
func QueryUserProfile(db *sql.DB, id int) (*UserProfile, error) {
    query := `
        SELECT id, username, settings, tags
        FROM user_profiles 
        WHERE id = $1`
    
    profile := &UserProfile{}
    err := db.QueryRow(query, id).Scan(
        &profile.ID,
        &profile.Username,
        &profile.Settings,
        &profile.Tags,
    )
    
    if err != nil {
        return nil, fmt.Errorf("查询用户配置失败: %w", err)
    }
    
    return profile, nil
}

4. 动态查询构建

4.1 查询构建器

go 复制代码
package database

import (
    "fmt"
    "strings"
)

// QueryBuilder 查询构建器
type QueryBuilder struct {
    table      string
    columns    []string
    conditions []string
    args       []interface{}
    orderBy    []string
    limit      int
    offset     int
}

// NewQueryBuilder 创建查询构建器
func NewQueryBuilder(table string) *QueryBuilder {
    return &QueryBuilder{
        table:   table,
        columns: []string{"*"},
    }
}

// Select 设置查询列
func (qb *QueryBuilder) Select(columns ...string) *QueryBuilder {
    qb.columns = columns
    return qb
}

// Where 添加 WHERE 条件
func (qb *QueryBuilder) Where(condition string, args ...interface{}) *QueryBuilder {
    qb.conditions = append(qb.conditions, condition)
    qb.args = append(qb.args, args...)
    return qb
}

// WhereIn 添加 IN 条件
func (qb *QueryBuilder) WhereIn(column string, values []interface{}) *QueryBuilder {
    if len(values) == 0 {
        return qb
    }
    
    placeholders := make([]string, len(values))
    for i := range values {
        placeholders[i] = fmt.Sprintf("$%d", len(qb.args)+i+1)
    }
    
    condition := fmt.Sprintf("%s IN (%s)", column, strings.Join(placeholders, ","))
    qb.conditions = append(qb.conditions, condition)
    qb.args = append(qb.args, values...)
    
    return qb
}

// WhereLike 添加 LIKE 条件
func (qb *QueryBuilder) WhereLike(column, pattern string) *QueryBuilder {
    condition := fmt.Sprintf("%s LIKE $%d", column, len(qb.args)+1)
    qb.conditions = append(qb.conditions, condition)
    qb.args = append(qb.args, "%"+pattern+"%")
    return qb
}

// OrderBy 添加排序
func (qb *QueryBuilder) OrderBy(column, direction string) *QueryBuilder {
    qb.orderBy = append(qb.orderBy, fmt.Sprintf("%s %s", column, direction))
    return qb
}

// Limit 设置限制
func (qb *QueryBuilder) Limit(limit int) *QueryBuilder {
    qb.limit = limit
    return qb
}

// Offset 设置偏移
func (qb *QueryBuilder) Offset(offset int) *QueryBuilder {
    qb.offset = offset
    return qb
}

// Build 构建查询语句
func (qb *QueryBuilder) Build() (string, []interface{}) {
    query := fmt.Sprintf("SELECT %s FROM %s", strings.Join(qb.columns, ", "), qb.table)
    
    if len(qb.conditions) > 0 {
        query += " WHERE " + strings.Join(qb.conditions, " AND ")
    }
    
    if len(qb.orderBy) > 0 {
        query += " ORDER BY " + strings.Join(qb.orderBy, ", ")
    }
    
    if qb.limit > 0 {
        query += fmt.Sprintf(" LIMIT %d", qb.limit)
    }
    
    if qb.offset > 0 {
        query += fmt.Sprintf(" OFFSET %d", qb.offset)
    }
    
    return query, qb.args
}

// 使用示例
func QueryUsersWithBuilder(db *sql.DB, filters map[string]interface{}) ([]*User, error) {
    qb := NewQueryBuilder("users").
        Select("id", "username", "email", "age", "created_at", "is_active")
    
    // 动态添加条件
    if minAge, ok := filters["min_age"].(int); ok {
        qb.Where("age >= $"+fmt.Sprintf("%d", len(qb.args)+1), minAge)
    }
    
    if username, ok := filters["username"].(string); ok {
        qb.WhereLike("username", username)
    }
    
    if isActive, ok := filters["is_active"].(bool); ok {
        qb.Where("is_active = $"+fmt.Sprintf("%d", len(qb.args)+1), isActive)
    }
    
    if ids, ok := filters["ids"].([]interface{}); ok {
        qb.WhereIn("id", ids)
    }
    
    // 添加排序和分页
    qb.OrderBy("created_at", "DESC")
    
    if limit, ok := filters["limit"].(int); ok {
        qb.Limit(limit)
    }
    
    if offset, ok := filters["offset"].(int); ok {
        qb.Offset(offset)
    }
    
    query, args := qb.Build()
    
    rows, err := db.Query(query, args...)
    if err != nil {
        return nil, fmt.Errorf("查询失败: %w", err)
    }
    defer rows.Close()
    
    var users []*User
    for rows.Next() {
        user := &User{}
        err := rows.Scan(
            &user.ID,
            &user.Username,
            &user.Email,
            &user.Age,
            &user.CreatedAt,
            &user.IsActive,
        )
        if err != nil {
            return nil, fmt.Errorf("扫描行失败: %w", err)
        }
        users = append(users, user)
    }
    
    return users, rows.Err()
}

4.2 条件构建器

go 复制代码
package database

import (
    "fmt"
    "strings"
)

// Condition 查询条件
type Condition struct {
    SQL  string
    Args []interface{}
}

// ConditionBuilder 条件构建器
type ConditionBuilder struct {
    conditions []Condition
    operator   string
}

// NewConditionBuilder 创建条件构建器
func NewConditionBuilder(operator string) *ConditionBuilder {
    return &ConditionBuilder{
        operator: operator,
    }
}

// And 创建 AND 条件构建器
func And() *ConditionBuilder {
    return NewConditionBuilder("AND")
}

// Or 创建 OR 条件构建器
func Or() *ConditionBuilder {
    return NewConditionBuilder("OR")
}

// Add 添加条件
func (cb *ConditionBuilder) Add(sql string, args ...interface{}) *ConditionBuilder {
    cb.conditions = append(cb.conditions, Condition{
        SQL:  sql,
        Args: args,
    })
    return cb
}

// AddIf 条件性添加条件
func (cb *ConditionBuilder) AddIf(condition bool, sql string, args ...interface{}) *ConditionBuilder {
    if condition {
        cb.Add(sql, args...)
    }
    return cb
}

// Equal 添加等于条件
func (cb *ConditionBuilder) Equal(column string, value interface{}) *ConditionBuilder {
    return cb.Add(fmt.Sprintf("%s = ?", column), value)
}

// NotEqual 添加不等于条件
func (cb *ConditionBuilder) NotEqual(column string, value interface{}) *ConditionBuilder {
    return cb.Add(fmt.Sprintf("%s != ?", column), value)
}

// GreaterThan 添加大于条件
func (cb *ConditionBuilder) GreaterThan(column string, value interface{}) *ConditionBuilder {
    return cb.Add(fmt.Sprintf("%s > ?", column), value)
}

// LessThan 添加小于条件
func (cb *ConditionBuilder) LessThan(column string, value interface{}) *ConditionBuilder {
    return cb.Add(fmt.Sprintf("%s < ?", column), value)
}

// Like 添加 LIKE 条件
func (cb *ConditionBuilder) Like(column, pattern string) *ConditionBuilder {
    return cb.Add(fmt.Sprintf("%s LIKE ?", column), "%"+pattern+"%")
}

// In 添加 IN 条件
func (cb *ConditionBuilder) In(column string, values []interface{}) *ConditionBuilder {
    if len(values) == 0 {
        return cb
    }
    
    placeholders := strings.Repeat("?,", len(values))
    placeholders = placeholders[:len(placeholders)-1] // 移除最后的逗号
    
    return cb.Add(fmt.Sprintf("%s IN (%s)", column, placeholders), values...)
}

// IsNull 添加 IS NULL 条件
func (cb *ConditionBuilder) IsNull(column string) *ConditionBuilder {
    return cb.Add(fmt.Sprintf("%s IS NULL", column))
}

// IsNotNull 添加 IS NOT NULL 条件
func (cb *ConditionBuilder) IsNotNull(column string) *ConditionBuilder {
    return cb.Add(fmt.Sprintf("%s IS NOT NULL", column))
}

// Build 构建条件
func (cb *ConditionBuilder) Build() (string, []interface{}) {
    if len(cb.conditions) == 0 {
        return "", nil
    }
    
    var sqlParts []string
    var args []interface{}
    
    for _, condition := range cb.conditions {
        sqlParts = append(sqlParts, condition.SQL)
        args = append(args, condition.Args...)
    }
    
    sql := strings.Join(sqlParts, " "+cb.operator+" ")
    return "(" + sql + ")", args
}

// 使用示例
func QueryUsersWithConditions(db *sql.DB, searchParams map[string]interface{}) ([]*User, error) {
    baseQuery := "SELECT id, username, email, age, created_at, is_active FROM users"
    
    // 构建复杂条件
    mainCondition := And()
    
    // 年龄范围
    if minAge, ok := searchParams["min_age"].(int); ok {
        mainCondition.GreaterThan("age", minAge)
    }
    
    if maxAge, ok := searchParams["max_age"].(int); ok {
        mainCondition.LessThan("age", maxAge)
    }
    
    // 用户名或邮箱搜索
    if search, ok := searchParams["search"].(string); ok {
        searchCondition := Or().
            Like("username", search).
            Like("email", search)
        
        searchSQL, searchArgs := searchCondition.Build()
        if searchSQL != "" {
            mainCondition.Add(searchSQL, searchArgs...)
        }
    }
    
    // 状态过滤
    if isActive, ok := searchParams["is_active"].(bool); ok {
        mainCondition.Equal("is_active", isActive)
    }
    
    // ID 列表
    if ids, ok := searchParams["ids"].([]interface{}); ok {
        mainCondition.In("id", ids)
    }
    
    // 构建最终查询
    whereSQL, args := mainCondition.Build()
    if whereSQL != "" {
        baseQuery += " WHERE " + whereSQL
    }
    
    baseQuery += " ORDER BY created_at DESC"
    
    // 分页
    if limit, ok := searchParams["limit"].(int); ok {
        baseQuery += fmt.Sprintf(" LIMIT %d", limit)
    }
    
    if offset, ok := searchParams["offset"].(int); ok {
        baseQuery += fmt.Sprintf(" OFFSET %d", offset)
    }
    
    rows, err := db.Query(baseQuery, args...)
    if err != nil {
        return nil, fmt.Errorf("查询失败: %w", err)
    }
    defer rows.Close()
    
    var users []*User
    for rows.Next() {
        user := &User{}
        err := rows.Scan(
            &user.ID,
            &user.Username,
            &user.Email,
            &user.Age,
            &user.CreatedAt,
            &user.IsActive,
        )
        if err != nil {
            return nil, fmt.Errorf("扫描行失败: %w", err)
        }
        users = append(users, user)
    }
    
    return users, rows.Err()
}

5. 预编译语句

5.1 基本预编译语句

go 复制代码
package database

import (
    "database/sql"
    "fmt"
)

// PreparedStatements 预编译语句管理器
type PreparedStatements struct {
    db    *sql.DB
    stmts map[string]*sql.Stmt
}

// NewPreparedStatements 创建预编译语句管理器
func NewPreparedStatements(db *sql.DB) *PreparedStatements {
    return &PreparedStatements{
        db:    db,
        stmts: make(map[string]*sql.Stmt),
    }
}

// Prepare 预编译语句
func (ps *PreparedStatements) Prepare(name, query string) error {
    stmt, err := ps.db.Prepare(query)
    if err != nil {
        return fmt.Errorf("预编译语句失败 [%s]: %w", name, err)
    }
    
    // 如果已存在,先关闭旧的
    if oldStmt, exists := ps.stmts[name]; exists {
        oldStmt.Close()
    }
    
    ps.stmts[name] = stmt
    return nil
}

// Get 获取预编译语句
func (ps *PreparedStatements) Get(name string) (*sql.Stmt, error) {
    stmt, exists := ps.stmts[name]
    if !exists {
        return nil, fmt.Errorf("预编译语句不存在: %s", name)
    }
    return stmt, nil
}

// Close 关闭所有预编译语句
func (ps *PreparedStatements) Close() error {
    var lastErr error
    for name, stmt := range ps.stmts {
        if err := stmt.Close(); err != nil {
            lastErr = fmt.Errorf("关闭预编译语句失败 [%s]: %w", name, err)
        }
    }
    ps.stmts = make(map[string]*sql.Stmt)
    return lastErr
}

// UserRepository 使用预编译语句的用户仓库
type UserRepository struct {
    db    *sql.DB
    stmts *PreparedStatements
}

// NewUserRepository 创建用户仓库
func NewUserRepository(db *sql.DB) (*UserRepository, error) {
    stmts := NewPreparedStatements(db)
    
    // 预编译常用语句
    queries := map[string]string{
        "insert_user": `
            INSERT INTO users (username, email, age, is_active) 
            VALUES ($1, $2, $3, $4) 
            RETURNING id, created_at`,
        "get_user_by_id": `
            SELECT id, username, email, age, created_at, is_active 
            FROM users WHERE id = $1`,
        "get_user_by_email": `
            SELECT id, username, email, age, created_at, is_active 
            FROM users WHERE email = $1`,
        "update_user": `
            UPDATE users 
            SET username = $2, email = $3, age = $4 
            WHERE id = $1`,
        "delete_user": `
            DELETE FROM users WHERE id = $1`,
        "list_users": `
            SELECT id, username, email, age, created_at, is_active 
            FROM users 
            ORDER BY created_at DESC 
            LIMIT $1 OFFSET $2`,
    }
    
    for name, query := range queries {
        if err := stmts.Prepare(name, query); err != nil {
            stmts.Close()
            return nil, err
        }
    }
    
    return &UserRepository{
        db:    db,
        stmts: stmts,
    }, nil
}

// CreateUser 创建用户
func (ur *UserRepository) CreateUser(username, email string, age int, isActive bool) (*User, error) {
    stmt, err := ur.stmts.Get("insert_user")
    if err != nil {
        return nil, err
    }
    
    user := &User{
        Username: username,
        Email:    email,
        Age:      age,
        IsActive: isActive,
    }
    
    err = stmt.QueryRow(username, email, age, isActive).Scan(&user.ID, &user.CreatedAt)
    if err != nil {
        return nil, fmt.Errorf("创建用户失败: %w", err)
    }
    
    return user, nil
}

// GetUserByID 根据ID获取用户
func (ur *UserRepository) GetUserByID(id int) (*User, error) {
    stmt, err := ur.stmts.Get("get_user_by_id")
    if err != nil {
        return nil, err
    }
    
    user := &User{}
    err = stmt.QueryRow(id).Scan(
        &user.ID,
        &user.Username,
        &user.Email,
        &user.Age,
        &user.CreatedAt,
        &user.IsActive,
    )
    
    if err != nil {
        return nil, fmt.Errorf("获取用户失败: %w", err)
    }
    
    return user, nil
}

// UpdateUser 更新用户
func (ur *UserRepository) UpdateUser(id int, username, email string, age int) error {
    stmt, err := ur.stmts.Get("update_user")
    if err != nil {
        return err
    }
    
    result, err := stmt.Exec(id, username, email, age)
    if err != nil {
        return fmt.Errorf("更新用户失败: %w", err)
    }
    
    rowsAffected, err := result.RowsAffected()
    if err != nil {
        return fmt.Errorf("获取影响行数失败: %w", err)
    }
    
    if rowsAffected == 0 {
        return fmt.Errorf("用户不存在: ID=%d", id)
    }
    
    return nil
}

// Close 关闭仓库
func (ur *UserRepository) Close() error {
    return ur.stmts.Close()
}

5.2 批量操作

go 复制代码
package database

import (
    "database/sql"
    "fmt"
)

// BatchInsertUsers 批量插入用户
func BatchInsertUsers(db *sql.DB, users []User) error {
    // 预编译插入语句
    stmt, err := db.Prepare(`
        INSERT INTO users (username, email, age, is_active) 
        VALUES ($1, $2, $3, $4)`)
    if err != nil {
        return fmt.Errorf("预编译语句失败: %w", err)
    }
    defer stmt.Close()
    
    // 开始事务
    tx, err := db.Begin()
    if err != nil {
        return fmt.Errorf("开始事务失败: %w", err)
    }
    defer tx.Rollback()
    
    // 在事务中使用预编译语句
    txStmt := tx.Stmt(stmt)
    
    for i, user := range users {
        _, err := txStmt.Exec(user.Username, user.Email, user.Age, user.IsActive)
        if err != nil {
            return fmt.Errorf("插入第 %d 个用户失败: %w", i+1, err)
        }
    }
    
    // 提交事务
    if err := tx.Commit(); err != nil {
        return fmt.Errorf("提交事务失败: %w", err)
    }
    
    return nil
}

// BatchUpdateUsers 批量更新用户
func BatchUpdateUsers(db *sql.DB, updates []struct {
    ID       int
    Username string
    Email    string
    Age      int
}) error {
    stmt, err := db.Prepare(`
        UPDATE users 
        SET username = $2, email = $3, age = $4 
        WHERE id = $1`)
    if err != nil {
        return fmt.Errorf("预编译语句失败: %w", err)
    }
    defer stmt.Close()
    
    tx, err := db.Begin()
    if err != nil {
        return fmt.Errorf("开始事务失败: %w", err)
    }
    defer tx.Rollback()
    
    txStmt := tx.Stmt(stmt)
    
    for i, update := range updates {
        result, err := txStmt.Exec(update.ID, update.Username, update.Email, update.Age)
        if err != nil {
            return fmt.Errorf("更新第 %d 个用户失败: %w", i+1, err)
        }
        
        rowsAffected, err := result.RowsAffected()
        if err != nil {
            return fmt.Errorf("获取第 %d 个用户影响行数失败: %w", i+1, err)
        }
        
        if rowsAffected == 0 {
            return fmt.Errorf("第 %d 个用户不存在: ID=%d", i+1, update.ID)
        }
    }
    
    if err := tx.Commit(); err != nil {
        return fmt.Errorf("提交事务失败: %w", err)
    }
    
    return nil
}

6. 结果集处理

6.1 流式处理

go 复制代码
package database

import (
    "database/sql"
    "fmt"
)

// StreamProcessor 流式处理器
type StreamProcessor struct {
    db *sql.DB
}

// NewStreamProcessor 创建流式处理器
func NewStreamProcessor(db *sql.DB) *StreamProcessor {
    return &StreamProcessor{db: db}
}

// ProcessUsersStream 流式处理用户数据
func (sp *StreamProcessor) ProcessUsersStream(
    query string,
    args []interface{},
    processor func(*User) error,
) error {
    rows, err := sp.db.Query(query, args...)
    if err != nil {
        return fmt.Errorf("查询失败: %w", err)
    }
    defer rows.Close()
    
    for rows.Next() {
        user := &User{}
        err := rows.Scan(
            &user.ID,
            &user.Username,
            &user.Email,
            &user.Age,
            &user.CreatedAt,
            &user.IsActive,
        )
        if err != nil {
            return fmt.Errorf("扫描行失败: %w", err)
        }
        
        // 处理单个用户
        if err := processor(user); err != nil {
            return fmt.Errorf("处理用户失败 [ID=%d]: %w", user.ID, err)
        }
    }
    
    return rows.Err()
}

// ExportUsersToCSV 导出用户到 CSV(流式处理示例)
func (sp *StreamProcessor) ExportUsersToCSV(filename string) error {
    file, err := os.Create(filename)
    if err != nil {
        return fmt.Errorf("创建文件失败: %w", err)
    }
    defer file.Close()
    
    writer := csv.NewWriter(file)
    defer writer.Flush()
    
    // 写入 CSV 头部
    if err := writer.Write([]string{"ID", "Username", "Email", "Age", "Created At", "Is Active"}); err != nil {
        return fmt.Errorf("写入 CSV 头部失败: %w", err)
    }
    
    // 流式处理用户数据
    query := "SELECT id, username, email, age, created_at, is_active FROM users ORDER BY id"
    
    return sp.ProcessUsersStream(query, nil, func(user *User) error {
        record := []string{
            fmt.Sprintf("%d", user.ID),
            user.Username,
            user.Email,
            fmt.Sprintf("%d", user.Age),
            user.CreatedAt.Format("2006-01-02 15:04:05"),
            fmt.Sprintf("%t", user.IsActive),
        }
        
        return writer.Write(record)
    })
}

6.2 分页处理

go 复制代码
package database

import (
    "database/sql"
    "fmt"
)

// PageProcessor 分页处理器
type PageProcessor struct {
    db       *sql.DB
    pageSize int
}

// NewPageProcessor 创建分页处理器
func NewPageProcessor(db *sql.DB, pageSize int) *PageProcessor {
    return &PageProcessor{
        db:       db,
        pageSize: pageSize,
    }
}

// ProcessAllUsers 分页处理所有用户
func (pp *PageProcessor) ProcessAllUsers(processor func([]*User) error) error {
    offset := 0
    
    for {
        users, err := pp.getUsersPage(offset)
        if err != nil {
            return fmt.Errorf("获取用户页面失败 [offset=%d]: %w", offset, err)
        }
        
        // 如果没有更多数据,退出循环
        if len(users) == 0 {
            break
        }
        
        // 处理当前页面的用户
        if err := processor(users); err != nil {
            return fmt.Errorf("处理用户页面失败 [offset=%d]: %w", offset, err)
        }
        
        // 如果返回的数据少于页面大小,说明已经是最后一页
        if len(users) < pp.pageSize {
            break
        }
        
        offset += pp.pageSize
    }
    
    return nil
}

// getUsersPage 获取用户页面
func (pp *PageProcessor) getUsersPage(offset int) ([]*User, error) {
    query := `
        SELECT id, username, email, age, created_at, is_active 
        FROM users 
        ORDER BY id 
        LIMIT $1 OFFSET $2`
    
    rows, err := pp.db.Query(query, pp.pageSize, offset)
    if err != nil {
        return nil, err
    }
    defer rows.Close()
    
    var users []*User
    for rows.Next() {
        user := &User{}
        err := rows.Scan(
            &user.ID,
            &user.Username,
            &user.Email,
            &user.Age,
            &user.CreatedAt,
            &user.IsActive,
        )
        if err != nil {
            return nil, err
        }
        users = append(users, user)
    }
    
    return users, rows.Err()
}

// 使用示例:统计用户数据
func (pp *PageProcessor) CalculateUserStatistics() (map[string]interface{}, error) {
    stats := map[string]interface{}{
        "total_users":   0,
        "active_users":  0,
        "total_age":     0,
        "age_groups": map[string]int{
            "18-25": 0,
            "26-35": 0,
            "36-45": 0,
            "46+":   0,
        },
    }
    
    err := pp.ProcessAllUsers(func(users []*User) error {
        for _, user := range users {
            // 总用户数
            stats["total_users"] = stats["total_users"].(int) + 1
            
            // 活跃用户数
            if user.IsActive {
                stats["active_users"] = stats["active_users"].(int) + 1
            }
            
            // 年龄统计
            stats["total_age"] = stats["total_age"].(int) + user.Age
            
            // 年龄组统计
            ageGroups := stats["age_groups"].(map[string]int)
            switch {
            case user.Age >= 18 && user.Age <= 25:
                ageGroups["18-25"]++
            case user.Age >= 26 && user.Age <= 35:
                ageGroups["26-35"]++
            case user.Age >= 36 && user.Age <= 45:
                ageGroups["36-45"]++
            case user.Age >= 46:
                ageGroups["46+"]++
            }
        }
        return nil
    })
    
    if err != nil {
        return nil, err
    }
    
    // 计算平均年龄
    if totalUsers := stats["total_users"].(int); totalUsers > 0 {
        stats["average_age"] = float64(stats["total_age"].(int)) / float64(totalUsers)
    }
    
    return stats, nil
}

7. 性能优化

7.1 查询优化

go 复制代码
package database

import (
    "database/sql"
    "fmt"
    "time"
)

// QueryOptimizer 查询优化器
type QueryOptimizer struct {
    db *sql.DB
}

// NewQueryOptimizer 创建查询优化器
func NewQueryOptimizer(db *sql.DB) *QueryOptimizer {
    return &QueryOptimizer{db: db}
}

// ExplainQuery 分析查询执行计划
func (qo *QueryOptimizer) ExplainQuery(query string, args ...interface{}) (string, error) {
    explainQuery := "EXPLAIN (ANALYZE, BUFFERS) " + query
    
    row := qo.db.QueryRow(explainQuery, args...)
    
    var plan string
    err := row.Scan(&plan)
    if err != nil {
        return "", fmt.Errorf("分析查询失败: %w", err)
    }
    
    return plan, nil
}

// BenchmarkQuery 基准测试查询
func (qo *QueryOptimizer) BenchmarkQuery(query string, args []interface{}, iterations int) (time.Duration, error) {
    start := time.Now()
    
    for i := 0; i < iterations; i++ {
        rows, err := qo.db.Query(query, args...)
        if err != nil {
            return 0, fmt.Errorf("查询失败: %w", err)
        }
        
        // 消费所有结果
        for rows.Next() {
            // 不做任何处理,只是消费结果
        }
        rows.Close()
        
        if err := rows.Err(); err != nil {
            return 0, fmt.Errorf("行迭代错误: %w", err)
        }
    }
    
    duration := time.Since(start)
    return duration / time.Duration(iterations), nil
}

// OptimizedUserQueries 优化的用户查询
type OptimizedUserQueries struct {
    db *sql.DB
}

// NewOptimizedUserQueries 创建优化的用户查询
func NewOptimizedUserQueries(db *sql.DB) *OptimizedUserQueries {
    return &OptimizedUserQueries{db: db}
}

// GetActiveUsersWithIndex 使用索引的活跃用户查询
func (ouq *OptimizedUserQueries) GetActiveUsersWithIndex(limit int) ([]*User, error) {
    // 确保 is_active 字段有索引
    // CREATE INDEX idx_users_active ON users(is_active) WHERE is_active = true;
    
    query := `
        SELECT id, username, email, age, created_at, is_active 
        FROM users 
        WHERE is_active = true 
        ORDER BY created_at DESC 
        LIMIT $1`
    
    rows, err := ouq.db.Query(query, limit)
    if err != nil {
        return nil, fmt.Errorf("查询活跃用户失败: %w", err)
    }
    defer rows.Close()
    
    var users []*User
    for rows.Next() {
        user := &User{}
        err := rows.Scan(
            &user.ID,
            &user.Username,
            &user.Email,
            &user.Age,
            &user.CreatedAt,
            &user.IsActive,
        )
        if err != nil {
            return nil, fmt.Errorf("扫描行失败: %w", err)
        }
        users = append(users, user)
    }
    
    return users, rows.Err()
}

// GetUsersByAgeRange 按年龄范围查询用户(使用复合索引)
func (ouq *OptimizedUserQueries) GetUsersByAgeRange(minAge, maxAge int) ([]*User, error) {
    // 确保有复合索引
    // CREATE INDEX idx_users_age_active ON users(age, is_active);
    
    query := `
        SELECT id, username, email, age, created_at, is_active 
        FROM users 
        WHERE age BETWEEN $1 AND $2 
        AND is_active = true 
        ORDER BY age`
    
    rows, err := ouq.db.Query(query, minAge, maxAge)
    if err != nil {
        return nil, fmt.Errorf("按年龄范围查询用户失败: %w", err)
    }
    defer rows.Close()
    
    var users []*User
    for rows.Next() {
        user := &User{}
        err := rows.Scan(
            &user.ID,
            &user.Username,
            &user.Email,
            &user.Age,
            &user.CreatedAt,
            &user.IsActive,
        )
        if err != nil {
            return nil, fmt.Errorf("扫描行失败: %w", err)
        }
        users = append(users, user)
    }
    
    return users, rows.Err()
}

// GetUserCountByStatus 按状态统计用户数(避免全表扫描)
func (ouq *OptimizedUserQueries) GetUserCountByStatus() (map[string]int, error) {
    query := `
        SELECT 
            is_active,
            COUNT(*) as count
        FROM users 
        GROUP BY is_active`
    
    rows, err := ouq.db.Query(query)
    if err != nil {
        return nil, fmt.Errorf("统计用户状态失败: %w", err)
    }
    defer rows.Close()
    
    result := make(map[string]int)
    for rows.Next() {
        var isActive bool
        var count int
        
        err := rows.Scan(&isActive, &count)
        if err != nil {
            return nil, fmt.Errorf("扫描统计结果失败: %w", err)
        }
        
        if isActive {
            result["active"] = count
        } else {
            result["inactive"] = count
        }
    }
    
    return result, rows.Err()
}

8. 完整示例

8.1 用户管理系统

go 复制代码
package main

import (
    "database/sql"
    "fmt"
    "log"
    "time"
    
    _ "github.com/lib/pq"
)

func main() {
    // 连接数据库
    dsn := "host=localhost port=5432 user=postgres password=password dbname=testdb sslmode=disable"
    db, err := sql.Open("postgres", dsn)
    if err != nil {
        log.Fatal("连接数据库失败:", err)
    }
    defer db.Close()
    
    // 配置连接池
    db.SetMaxOpenConns(25)
    db.SetMaxIdleConns(10)
    db.SetConnMaxLifetime(5 * time.Minute)
    
    // 测试连接
    if err := db.Ping(); err != nil {
        log.Fatal("数据库连接测试失败:", err)
    }
    
    // 创建表
    if err := createTables(db); err != nil {
        log.Fatal("创建表失败:", err)
    }
    
    // 演示各种查询操作
    demonstrateQueries(db)
}

// createTables 创建表
func createTables(db *sql.DB) error {
    createUserTable := `
    CREATE TABLE IF NOT EXISTS users (
        id SERIAL PRIMARY KEY,
        username VARCHAR(50) UNIQUE NOT NULL,
        email VARCHAR(100) UNIQUE NOT NULL,
        age INTEGER,
        phone VARCHAR(20),
        avatar TEXT,
        is_active BOOLEAN DEFAULT true,
        settings JSONB,
        tags TEXT,
        created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
        updated_at TIMESTAMP
    )`
    
    _, err := db.Exec(createUserTable)
    if err != nil {
        return fmt.Errorf("创建用户表失败: %w", err)
    }
    
    // 创建索引
    indexes := []string{
        "CREATE INDEX IF NOT EXISTS idx_users_email ON users(email)",
        "CREATE INDEX IF NOT EXISTS idx_users_active ON users(is_active) WHERE is_active = true",
        "CREATE INDEX IF NOT EXISTS idx_users_age_active ON users(age, is_active)",
        "CREATE INDEX IF NOT EXISTS idx_users_created_at ON users(created_at)",
    }
    
    for _, index := range indexes {
        if _, err := db.Exec(index); err != nil {
            return fmt.Errorf("创建索引失败: %w", err)
        }
    }
    
    return nil
}

// demonstrateQueries 演示查询操作
func demonstrateQueries(db *sql.DB) {
    fmt.Println("=== 数据库查询与扫描演示 ===")
    
    // 1. 插入测试数据
    fmt.Println("\n1. 插入测试数据...")
    insertTestData(db)
    
    // 2. 基本查询
    fmt.Println("\n2. 基本查询演示...")
    basicQueryDemo(db)
    
    // 3. 动态查询
    fmt.Println("\n3. 动态查询演示...")
    dynamicQueryDemo(db)
    
    // 4. 预编译语句
    fmt.Println("\n4. 预编译语句演示...")
    preparedStatementDemo(db)
    
    // 5. 分页查询
    fmt.Println("\n5. 分页查询演示...")
    paginationDemo(db)
    
    // 6. 聚合查询
    fmt.Println("\n6. 聚合查询演示...")
    aggregationDemo(db)
}

// insertTestData 插入测试数据
func insertTestData(db *sql.DB) {
    users := []struct {
        username string
        email    string
        age      int
        isActive bool
    }{
        {"alice", "alice@example.com", 25, true},
        {"bob", "bob@example.com", 30, true},
        {"charlie", "charlie@example.com", 35, false},
        {"diana", "diana@example.com", 28, true},
        {"eve", "eve@example.com", 32, false},
    }
    
    for _, user := range users {
        _, err := db.Exec(
            "INSERT INTO users (username, email, age, is_active) VALUES ($1, $2, $3, $4) ON CONFLICT (username) DO NOTHING",
            user.username, user.email, user.age, user.isActive)
        if err != nil {
            log.Printf("插入用户失败: %v", err)
        }
    }
    
    fmt.Println("测试数据插入完成")
}

// basicQueryDemo 基本查询演示
func basicQueryDemo(db *sql.DB) {
    // 查询所有用户
    rows, err := db.Query("SELECT id, username, email, age, is_active FROM users ORDER BY id")
    if err != nil {
        log.Printf("查询失败: %v", err)
        return
    }
    defer rows.Close()
    
    fmt.Println("所有用户:")
    for rows.Next() {
        var id, age int
        var username, email string
        var isActive bool
        
        err := rows.Scan(&id, &username, &email, &age, &isActive)
        if err != nil {
            log.Printf("扫描行失败: %v", err)
            continue
        }
        
        fmt.Printf("  ID: %d, 用户名: %s, 邮箱: %s, 年龄: %d, 活跃: %t\n",
            id, username, email, age, isActive)
    }
    
    // 单行查询
    var count int
    err = db.QueryRow("SELECT COUNT(*) FROM users WHERE is_active = true").Scan(&count)
    if err != nil {
        log.Printf("查询活跃用户数失败: %v", err)
        return
    }
    
    fmt.Printf("活跃用户数: %d\n", count)
}

// dynamicQueryDemo 动态查询演示
func dynamicQueryDemo(db *sql.DB) {
    // 使用查询构建器
    qb := NewQueryBuilder("users").
        Select("id", "username", "email", "age").
        Where("age > $1", 25).
        Where("is_active = $2", true).
        OrderBy("age", "ASC").
        Limit(3)
    
    query, args := qb.Build()
    fmt.Printf("动态构建的查询: %s\n", query)
    fmt.Printf("参数: %v\n", args)
    
    rows, err := db.Query(query, args...)
    if err != nil {
        log.Printf("动态查询失败: %v", err)
        return
    }
    defer rows.Close()
    
    fmt.Println("查询结果:")
    for rows.Next() {
        var id, age int
        var username, email string
        
        err := rows.Scan(&id, &username, &email, &age)
        if err != nil {
            log.Printf("扫描行失败: %v", err)
            continue
        }
        
        fmt.Printf("  ID: %d, 用户名: %s, 年龄: %d\n", id, username, age)
    }
}

// preparedStatementDemo 预编译语句演示
func preparedStatementDemo(db *sql.DB) {
    // 预编译查询语句
    stmt, err := db.Prepare("SELECT username, email FROM users WHERE age = $1")
    if err != nil {
        log.Printf("预编译语句失败: %v", err)
        return
    }
    defer stmt.Close()
    
    // 使用预编译语句查询不同年龄的用户
    ages := []int{25, 30, 35}
    
    for _, age := range ages {
        rows, err := stmt.Query(age)
        if err != nil {
            log.Printf("查询年龄 %d 的用户失败: %v", age, err)
            continue
        }
        
        fmt.Printf("年龄为 %d 的用户:\n", age)
        for rows.Next() {
            var username, email string
            err := rows.Scan(&username, &email)
            if err != nil {
                log.Printf("扫描行失败: %v", err)
                continue
            }
            fmt.Printf("  用户名: %s, 邮箱: %s\n", username, email)
        }
        rows.Close()
    }
}

// paginationDemo 分页查询演示
func paginationDemo(db *sql.DB) {
    pageSize := 2
    page := 1
    
    for {
        offset := (page - 1) * pageSize
        
        rows, err := db.Query(
            "SELECT id, username, email FROM users ORDER BY id LIMIT $1 OFFSET $2",
            pageSize, offset)
        if err != nil {
            log.Printf("分页查询失败: %v", err)
            return
        }
        
        var users []struct {
            ID       int
            Username string
            Email    string
        }
        
        for rows.Next() {
            var user struct {
                ID       int
                Username string
                Email    string
            }
            err := rows.Scan(&user.ID, &user.Username, &user.Email)
            if err != nil {
                log.Printf("扫描行失败: %v", err)
                continue
            }
            users = append(users, user)
        }
        rows.Close()
        
        if len(users) == 0 {
            break
        }
        
        fmt.Printf("第 %d 页 (每页 %d 条):\n", page, pageSize)
        for _, user := range users {
            fmt.Printf("  ID: %d, 用户名: %s, 邮箱: %s\n", user.ID, user.Username, user.Email)
        }
        
        if len(users) < pageSize {
            break
        }
        
        page++
    }
}

// aggregationDemo 聚合查询演示
func aggregationDemo(db *sql.DB) {
    query := `
        SELECT 
            COUNT(*) as total_users,
            COUNT(CASE WHEN is_active = true THEN 1 END) as active_users,
            AVG(age) as average_age,
            MIN(age) as min_age,
            MAX(age) as max_age
        FROM users`
    
    var totalUsers, activeUsers int
    var avgAge, minAge, maxAge float64
    
    err := db.QueryRow(query).Scan(&totalUsers, &activeUsers, &avgAge, &minAge, &maxAge)
    if err != nil {
        log.Printf("聚合查询失败: %v", err)
        return
    }
    
    fmt.Printf("用户统计信息:\n")
    fmt.Printf("  总用户数: %d\n", totalUsers)
    fmt.Printf("  活跃用户数: %d\n", activeUsers)
    fmt.Printf("  平均年龄: %.2f\n", avgAge)
    fmt.Printf("  最小年龄: %.0f\n", minAge)
    fmt.Printf("  最大年龄: %.0f\n", maxAge)
}

9. 最佳实践总结

9.1 查询优化建议

  1. 使用适当的索引

    • 为经常查询的列创建索引
    • 使用复合索引优化多列查询
    • 避免在小表上创建过多索引
  2. 查询语句优化

    • 只查询需要的列,避免 SELECT *
    • 使用 LIMIT 限制结果集大小
    • 合理使用 WHERE 条件过滤数据
  3. 连接池配置

    • 根据应用负载调整连接池大小
    • 设置合适的连接生命周期
    • 监控连接池使用情况

9.2 扫描最佳实践

  1. 正确处理 NULL 值

    • 使用 sql.NullStringsql.NullInt64 等类型
    • 实现自定义扫描类型处理复杂数据
  2. 错误处理

    • 始终检查 rows.Err()
    • 正确处理 sql.ErrNoRows
    • 使用适当的错误包装
  3. 资源管理

    • 及时关闭 rowsstmt
    • 使用 defer 确保资源释放
    • 避免资源泄漏

9.3 性能优化技巧

  1. 批量操作

    • 使用事务进行批量插入/更新
    • 预编译语句提高重复操作性能
    • 合理使用批处理大小
  2. 内存管理

    • 流式处理大结果集
    • 分页处理避免内存溢出
    • 及时释放不需要的数据
  3. 监控和调试

    • 使用 EXPLAIN 分析查询计划
    • 监控慢查询
    • 定期分析数据库性能

总结

本章详细介绍了 Go 语言中数据库查询与扫描的各种技术和最佳实践。通过掌握这些技术,你可以:

  1. 高效执行各种类型的数据库查询
  2. 正确处理查询结果的扫描和类型转换
  3. 构建灵活的动态查询系统
  4. 优化查询性能和资源使用
  5. 处理大数据集和复杂查询场景

这些技术为构建高性能的数据库应用程序提供了坚实的基础。在实际开发中,应该根据具体的业务需求和性能要求,选择合适的查询和扫描策略。

相关推荐
@大迁世界2 小时前
第06章:Dynamic Components(动态组件)
前端·javascript·vue.js·前端框架·ecmascript
国服第二切图仔3 小时前
Rust实战开发之图形界面开发入门(egui crate)
开发语言·后端·rust
程序员爱钓鱼3 小时前
Python编程实战:文件读写(文本/二进制)详解与实战
后端·python·ipython
Zhangzy@3 小时前
Rust 依赖管理与版本控制
开发语言·后端·rust
Momentary_SixthSense3 小时前
rust表格文件处理
开发语言·rust
小八四爱吃甜食3 小时前
【R语言】构建GO、KEGG相关不同物种的R包
开发语言·golang·r语言
音符犹如代码3 小时前
ArrayList常见面试题二
java·开发语言·面试·职场和发展
尘缘浮梦3 小时前
RobotFramework框架环境搭建
linux·开发语言·python
gustt3 小时前
用小程序搭建博客首页:从数据驱动到界面展示
android·前端·微信小程序