Golang中DB order by子句使用问题探讨
Golang中DB order by子句使用问题探讨
qry := `
SELECT id, uuid, name
FROM users
ORDER BY ?
LIMIT ?
OFFSET ?
`
limit := getLimit() // 10 (int)
offset := getOffset() // 0 (int)
order := getOrder() // id DESC (string)
rows, err := db.QueryContext(ctx, qry, order, limit, offset)
当我运行上面的代码时,ORDER BY 子句不会影响结果,因为 id DESC 被单引号包裹,如下面的数据库日志所示。是否有解决此问题的方法(不包括使用 fmt.Sprintf)?单引号是这里的问题所在。
SELECT id, uuid, name
FROM users
ORDER BY 'id DESC'
LIMIT 10
OFFSET 0
更多关于Golang中DB order by子句使用问题探讨的实战教程也可以访问 https://www.itying.com/category-94-b0.html
3 回复
尝试将其拆分为两个参数,例如在您的SQL中:... ORDER BY ? ?,然后将 getOrder 的结果拆分为两个参数:order 和 direction。
更多关于Golang中DB order by子句使用问题探讨的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html
在Go的database/sql包中,占位符?只能用于值参数,不能用于SQL关键字或标识符。ORDER BY子句中的列名和排序方向会被当作字符串值处理,导致被单引号包裹。
以下是几种解决方案:
方案1:使用字符串拼接(但避免SQL注入)
qry := fmt.Sprintf(`
SELECT id, uuid, name
FROM users
ORDER BY %s
LIMIT ?
OFFSET ?
`, sanitizeOrderBy(order)) // 需要实现sanitizeOrderBy函数验证order参数
rows, err := db.QueryContext(ctx, qry, limit, offset)
方案2:使用白名单验证
func buildQuery(order string) (string, error) {
allowedOrders := map[string]bool{
"id ASC": true, "id DESC": true,
"name ASC": true, "name DESC": true,
"created_at ASC": true, "created_at DESC": true,
}
if !allowedOrders[order] {
return "", fmt.Errorf("invalid order clause")
}
return fmt.Sprintf(`
SELECT id, uuid, name
FROM users
ORDER BY %s
LIMIT ?
OFFSET ?
`, order), nil
}
qry, err := buildQuery(order)
if err != nil {
// 处理错误
}
rows, err := db.QueryContext(ctx, qry, limit, offset)
方案3:使用查询构建器
// 使用第三方库如squirrel
import "github.com/Masterminds/squirrel"
qb := squirrel.Select("id", "uuid", "name").
From("users").
OrderBy(order).
Limit(uint64(limit)).
Offset(uint64(offset))
qry, args, err := qb.ToSql()
if err != nil {
// 处理错误
}
rows, err := db.QueryContext(ctx, qry, args...)
方案4:分拆列名和排序方向
func buildOrderClause(column, direction string) (string, error) {
allowedColumns := map[string]bool{"id": true, "name": true, "created_at": true}
allowedDirections := map[string]bool{"ASC": true, "DESC": true}
if !allowedColumns[column] || !allowedDirections[direction] {
return "", fmt.Errorf("invalid order parameters")
}
return fmt.Sprintf("%s %s", column, direction), nil
}
orderClause, err := buildOrderClause("id", "DESC")
if err != nil {
// 处理错误
}
qry := fmt.Sprintf(`
SELECT id, uuid, name
FROM users
ORDER BY %s
LIMIT ?
OFFSET ?
`, orderClause)
rows, err := db.QueryContext(ctx, qry, limit, offset)
方案5:使用CASE语句(复杂但安全)
qry := `
SELECT id, uuid, name
FROM users
ORDER BY
CASE WHEN ? = 'id_asc' THEN id END ASC,
CASE WHEN ? = 'id_desc' THEN id END DESC,
CASE WHEN ? = 'name_asc' THEN name END ASC,
CASE WHEN ? = 'name_desc' THEN name END DESC
LIMIT ?
OFFSET ?
`
// 使用预定义的排序选项
orderParam := "id_desc" // 从配置或验证中获取
rows, err := db.QueryContext(ctx, qry, orderParam, orderParam, orderParam, orderParam, limit, offset)
推荐使用方案2或方案4,它们在安全性和灵活性之间取得了较好的平衡。方案3使用查询构建器是最佳实践,但需要引入第三方依赖。


