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

使用Postgres与sqlx在事务中复用过滤数据执行多查询

解决方案:PostgreSQL + sqlx 单次过滤完成多维度查询

针对你的需求,这里提供两种高效且易实现的方案,都能避免重复过滤,同时解决结构体映射和临时表的问题:

方案一:WITH 语句 + 多结果集扫描

PostgreSQL 的 WITH 子句会将过滤结果缓存(只读场景下会被优化为单次扫描),你可以在同一个 SQL 语句中返回总数、分组统计、分页数据三个结果集,再用 sqlx 依次扫描每个结果集映射到对应结构体。

SQL 示例

WITH filtered_requests AS (
    SELECT id, status, created_at, user_id 
    FROM request
    WHERE created_at >= $1 AND status = ANY($2)
)
SELECT COUNT(*) FROM filtered_requests; -- 总数结果集
SELECT status, COUNT(*) AS count FROM filtered_requests GROUP BY status; -- 分组统计结果集
SELECT id, status, created_at, user_id FROM filtered_requests ORDER BY created_at DESC LIMIT $3 OFFSET $4; -- 分页数据结果集

sqlx 代码实现

先定义对应的结构体:

type Request struct {
    ID        int64     `db:"id"`
    Status    string    `db:"status"`
    CreatedAt time.Time `db:"created_at"`
    UserID    int64     `db:"user_id"`
}

type StatusCount struct {
    Status string `db:"status"`
    Count  int    `db:"count"`
}

然后处理多结果集:

ctx := context.Background()
startDate := time.Date(2024, 1, 1, 0, 0, 0, 0, time.UTC)
statuses := []string{"pending", "completed"}
limit := 20
offset := 0

query := `
WITH filtered_requests AS (
    SELECT id, status, created_at, user_id 
    FROM request
    WHERE created_at >= $1 AND status = ANY($2)
)
SELECT COUNT(*) FROM filtered_requests;
SELECT status, COUNT(*) AS count FROM filtered_requests GROUP BY status;
SELECT id, status, created_at, user_id FROM filtered_requests ORDER BY created_at DESC LIMIT $3 OFFSET $4;
`

rows, err := db.QueryContext(ctx, query, startDate, statuses, limit, offset)
if err != nil {
    // 处理错误
}
defer rows.Close()

// 读取总数
var total int
if rows.Next() {
    if err := rows.Scan(&total); err != nil {
        // 处理错误
    }
}

// 切换到分组统计结果集
rows.NextResultSet()
var statusCounts []StatusCount
for rows.Next() {
    var sc StatusCount
    if err := rows.Scan(&sc.Status, &sc.Count); err != nil {
        // 处理错误
    }
    statusCounts = append(statusCounts, sc)
}

// 切换到分页数据结果集
rows.NextResultSet()
var requests []Request
for rows.Next() {
    var req Request
    if err := rows.Scan(&req.ID, &req.Status, &req.CreatedAt, &req.UserID); err != nil {
        // 处理错误
    }
    requests = append(requests, req)
}

// 检查所有结果集的遍历错误
if err := rows.Err(); err != nil {
    // 处理错误
}

方案二:会话级临时表

PostgreSQL 的临时表默认是会话级的,无需显式事务,只要在同一个连接内即可访问。你可以先创建临时表存储过滤结果,再基于临时表执行三个查询,避免重复扫描原表。

sqlx 代码实现

ctx := context.Background()
startDate := time.Date(2024, 1, 1, 0, 0, 0, 0, time.UTC)
statuses := []string{"pending", "completed"}
limit := 20
offset := 0

// 从连接池获取单独连接(临时表仅在该连接内有效)
conn, err := db.Connx(ctx)
if err != nil {
    // 处理错误
}
defer conn.Close()

// 创建临时表并写入过滤结果
_, err = conn.ExecContext(ctx, `
CREATE TEMP TABLE filtered_requests AS
SELECT id, status, created_at, user_id 
FROM request
WHERE created_at >= $1 AND status = ANY($2)
`, startDate, statuses)
if err != nil {
    // 处理错误
}

// 查询总数
var total int
err = conn.QueryRowContext(ctx, "SELECT COUNT(*) FROM filtered_requests").Scan(&total)
if err != nil {
    // 处理错误
}

// 查询分组统计
var statusCounts []StatusCount
rows, err := conn.QueryContext(ctx, "SELECT status, COUNT(*) AS count FROM filtered_requests GROUP BY status")
if err != nil {
    // 处理错误
}
defer rows.Close()
for rows.Next() {
    var sc StatusCount
    if err := rows.Scan(&sc.Status, &sc.Count); err != nil {
        // 处理错误
    }
    statusCounts = append(statusCounts, sc)
}
if err := rows.Err(); err != nil {
    // 处理错误
}

// 查询分页数据
var requests []Request
rows, err = conn.QueryContext(ctx, `
SELECT id, status, created_at, user_id 
FROM filtered_requests 
ORDER BY created_at DESC LIMIT $1 OFFSET $2
`, limit, offset)
if err != nil {
    // 处理错误
}
defer rows.Close()
for rows.Next() {
    var req Request
    if err := rows.Scan(&req.ID, &req.Status, &req.CreatedAt, &req.UserID); err != nil {
        // 处理错误
    }
    requests = append(requests, req)
}
if err := rows.Err(); err != nil {
    // 处理错误
}

方案对比

方案优点缺点
WITH + 多结果集无需临时表,性能最优(PostgreSQL 自动优化)需处理多结果集切换,代码稍繁琐
会话级临时表逻辑清晰,每个查询独立,结构体映射简单需占用单独连接,临时表会占用少量内存

内容的提问来源于stack exchange,提问作者Nikola-Milovic

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.23 12:27:46