You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何使用sqlx与PostgreSQL记录参数而非占位符?

在sqlx中输出带实际参数的SQL日志

要解决sqlx日志里仅显示$1、$2占位符而非实际参数的问题,可通过以下两种方式实现:

一、手动编写SQL参数替换函数

自己实现辅助函数,将占位符替换为实际参数值,同时处理不同数据类型的格式和转义问题:

import (
    "fmt"
    "strings"
    "time"
    "log"
)

// 将带占位符的SQL和参数拼接成可直接执行的SQL语句
func formatSQLWithParams(query string, args ...interface{}) string {
    formattedQuery := query
    for idx, arg := range args {
        placeholder := fmt.Sprintf("$%d", idx+1)
        var paramStr string

        switch val := arg.(type) {
        case string:
            // 转义字符串中的单引号,避免SQL语法错误
            escapedVal := strings.ReplaceAll(val, "'", "''")
            paramStr = fmt.Sprintf("'%s'", escapedVal)
        case int, int8, int16, int32, int64, uint, uint8, uint16, uint32, uint64, float32, float64:
            // 数字类型直接转字符串,无需加引号
            paramStr = fmt.Sprintf("%v", val)
        case time.Time:
            // 日期类型转成数据库兼容的字符串格式
            paramStr = fmt.Sprintf("'%s'", val.Format("2006-01-02 15:04:05"))
        case nil:
            paramStr = "NULL"
        default:
            // 其他类型默认转成带引号的字符串,可根据需要扩展
            paramStr = fmt.Sprintf("'%v'", val)
        }

        formattedQuery = strings.ReplaceAll(formattedQuery, placeholder, paramStr)
    }
    return formattedQuery
}

使用示例

执行SQL时,调用该函数生成可直接复制验证的SQL并日志:

teamID := "ABC"
playerID := "123"
query := "SELECT * from players where team_id = $1 AND id = $2"

// 执行SQL
rows, err := db.QueryContext(ctx, query, teamID, playerID)
if err != nil {
    // 日志输出带实际参数的错误SQL
    log.Printf("SQL执行失败: %s", formatSQLWithParams(query, teamID, playerID))
    return err
}

// 可选:日志成功执行的SQL
log.Printf("执行SQL: %s", formatSQLWithParams(query, teamID, playerID))

二、包装sqlx DB对象实现自动日志

如果不想每次执行都手动调用函数,可以包装sqlx的DB或Tx对象,拦截所有查询请求,自动完成参数替换和日志输出:

import "context"
import "database/sql"

type LoggedDB struct {
    *sqlx.DB
}

func (db *LoggedDB) QueryContext(ctx context.Context, query string, args ...interface{}) (*sql.Rows, error) {
    formattedSQL := formatSQLWithParams(query, args...)
    log.Printf("执行SQL: %s", formattedSQL)
    return db.DB.QueryContext(ctx, query, args...)
}

func (db *LoggedDB) ExecContext(ctx context.Context, query string, args ...interface{}) (sql.Result, error) {
    formattedSQL := formatSQLWithParams(query, args...)
    log.Printf("执行SQL: %s", formattedSQL)
    return db.DB.ExecContext(ctx, query, args...)
}

// 其他需要拦截的方法(如QueryxContext、Exec等)可类似扩展

使用示例

// 将原sqlx.DB包装成LoggedDB
loggedDB := &LoggedDB{DB: db}

// 后续使用loggedDB执行SQL,会自动输出带参数的日志
rows, err := loggedDB.QueryContext(ctx, "SELECT * from players where team_id = $1 AND id = $2", "ABC", "123")

注意事项

  • 敏感信息脱敏:如果参数包含密码、手机号等敏感数据,需要在替换时进行脱敏处理,避免泄露。
  • 性能影响:频繁的字符串替换可能会带来轻微性能损耗,生产环境可根据需要控制日志级别(如仅Debug级别输出)。
  • 兼容性:该方法仅适用于sqlx使用$N格式占位符的场景(如PostgreSQL),如果是MySQL使用?占位符,需要修改占位符的匹配逻辑。

内容的提问来源于stack exchange,提问作者Fred Hors

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.06 22:30:48