Как с помощью sqlx и pgx корректно выполнить выборку по множеству пар значений столбцов (например WHERE (col1, col2) IN ((v1a, v1b), (v2a, v2b), ...)) без сложных ухищрений?

PostgreSQL v11.13 (переход на более новую версию <b>НЕ</b> предлагать), <br/> Go v1.26.4, <br/> github.com/jackc/pgx v5.10.0 <br/> github.com/jmoiron/sqlx v1.4.0 <br/>
  • 7 июля 2026 г. 19:00
Ответы на вопрос 2
Коротко — самый простой, безопасный и понятный путь — подставлять пары как список VALUES и передавать сами значения как параметры. Примерно так:

- Собираете слайс аргументов []interface{}.
- Динамически генерируете "( $1,$2 ),( $3,$4 ),..." для VALUES.
- Выполняете запрос через sqlx (или pgx), передавая args...

Пример для sqlx + pgx (через pgx stdlib) — поиск по множеству пар целых значений:

```go
import (
    "fmt"
    "strings"

    "github.com/jackc/pgx/v5/stdlib" // регистрирует драйвер "pgx"
    "github.com/jmoiron/sqlx"
)

type Row struct {
    Col1 int `db:"col1"`
    Col2 int `db:"col2"`
    // остальные поля...
}

func selectByPairs(db *sqlx.DB, pairs [][2]int) ([]Row, error) {
    if len(pairs) == 0 {
        return nil, nil // или вернуть пустой слайс / особую логику
    }

    args := make([]interface{}, 0, len(pairs)*2)
    vals := make([]string, 0, len(pairs))
    for i, p := range pairs {
        // placeholders: $1,$2  $3,$4 ...
        idx := i*2 + 1
        vals = append(vals, fmt.Sprintf("($%d,$%d)", idx, idx+1))
        args = append(args, p[0], p[1])
    }

    q := "SELECT * FROM my_table WHERE (col1, col2) IN (VALUES " + strings.Join(vals, ",") + ")"

    var out []Row
    if err := db.Select(&out, q, args...); err != nil {
        return nil, err
    }
    return out, nil
}
```

Пояснения и замечания:
- Такой подход безопасен: все значения идут через параметры, SQL-инъекции исключены.
- Этот синтаксис (WHERE (a,b) IN (VALUES (...),(...))) полностью поддерживается PostgreSQL 11.
- Если нужно сохранить порядок результатов, соответствующий порядку входных пар, можно добавлять индекс в VALUES и JOIN:

  q := "SELECT t.* FROM my_table t JOIN (VALUES ($1,$2,0), ($3,$4,1), ...) AS v(col1,col2,ord) USING (col1,col2) ORDER BY v.ord"

- Обработка пустого списка — отдельный случай: лучше заранее вернуть пустой результат или добавить условие LIKE "WHERE false".
- Если вы используете pgx напрямую (pgx.Conn / pgxpool), синтаксис $1,$2 остаётся таким же — подход аналогичен.
- Не используйте конкатенацию реальных значений в SQL строку — только placeholders.

Это простой, понятный и широко используемый способ без "магии" и без изменения версии PostgreSQL.
[]Key напрямую не передашь — либо composite type в pg + LoadType/RegisterType, либо в лоб: unnest на двух срезах без JSON
Похожие вопросы