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
相关产品推荐
相关产品推荐

