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

Go sql操作PostgreSQL:元组IN查询实现方法

在Go中执行PostgreSQL多列IN查询的正确方式

当然可以实现这种多列IN查询,直接字符串插值不行是因为Go的database/sql包要求参数化查询来避免SQL注入,同时PostgreSQL本身支持两种可靠方式处理这种场景:

方法一:使用unnest函数配合数组参数

利用PostgreSQL的unnest函数将数组参数拆分为行,实现多列匹配:

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

type Record struct {
    ID      int
    Updated time.Time
    // 其他字段根据表结构补充
}

func queryRecords(db *sql.DB) ([]Record, error) {
    query := `
        select *
        from records
        where (id, updated) in (
            select unnest($1::int[]), unnest($2::timestamp[])
        )
    `

    ids := []int{1, 2}
    updateTimes := []time.Time{
        time.Date(2019, 1, 1, 0, 0, 0, 0, time.UTC),
        time.Date(2019, 1, 2, 0, 0, 0, 0, time.UTC),
    }

    rows, err := db.Query(query, ids, updateTimes)
    if err != nil {
        return nil, err
    }
    defer rows.Close()

    var records []Record
    for rows.Next() {
        var r Record
        if err := rows.Scan(&r.ID, &r.Updated /* 其他字段 */); err != nil {
            return nil, err
        }
        records = append(records, r)
    }
    return records, rows.Err()
}

注意:两个数组的长度必须完全一致,PostgreSQL会按索引位置配对数组元素。

方法二:动态生成参数占位符

如果需要更直观的条件分组,可以动态生成每组条件的占位符,再将参数扁平化传入:

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

type Record struct {
    ID      int
    Updated time.Time
    // 其他字段根据表结构补充
}

type RecordKey struct {
    ID      int
    Updated time.Time
}

func queryRecords(db *sql.DB) ([]Record, error) {
    keys := []RecordKey{
        {1, time.Date(2019, 1, 1, 0, 0, 0, 0, time.UTC)},
        {2, time.Date(2019, 1, 2, 0, 0, 0, 0, time.UTC)},
    }

    // 生成占位符字符串,如"($1,$2), ($3,$4)"
    placeholders := make([]string, len(keys))
    args := make([]interface{}, 0, len(keys)*2)
    for i, key := range keys {
        paramIdx := i*2 + 1
        placeholders[i] = fmt.Sprintf("($%d, $%d)", paramIdx, paramIdx+1)
        args = append(args, key.ID, key.Updated)
    }
    placeholderStr := strings.Join(placeholders, ", ")

    query := fmt.Sprintf(`
        select *
        from records
        where (id, updated) in (%s)
    `, placeholderStr)

    rows, err := db.Query(query, args...)
    if err != nil {
        return nil, err
    }
    defer rows.Close()

    var records []Record
    for rows.Next() {
        var r Record
        if err := rows.Scan(&r.ID, &r.Updated /* 其他字段 */); err != nil {
            return nil, err
        }
        records = append(records, r)
    }
    return records, rows.Err()
}

关键提醒

绝对不要直接用字符串拼接把ID和时间值插入查询语句,这会引入严重的SQL注入风险。上面两种方法都是通过Go的参数化查询机制传递值,既安全又能正确适配PostgreSQL的类型要求。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.19 06:35:06