Use Composite IN Conditions with Postgres.js

For a dynamic list of value pairs, join the target table to a parameterized VALUES table. This preserves each pair as one unit and lets Postgres.js serialize the data without concatenating SQL text.

Last updated: October 6, 2026.

const pairs = [
  [101, 'open'],
  [205, 'closed'],
  [310, 'open'],
];

async function findTickets(pairs) {
  if (pairs.length === 0) return [];

  return sql`
    SELECT t.*
    FROM tickets AS t
    JOIN (VALUES ${sql(pairs)}) AS wanted(ticket_id, status)
      ON wanted.ticket_id = t.ticket_id
     AND wanted.status = t.status
  `;
}

The helper creates placeholders for the supplied values, while the column names and SQL structure remain fixed. Cast a generated column when PostgreSQL cannot infer a shared type, for example wanted.ticket_id::bigint. Return early when the pair list is empty rather than generating an empty VALUES clause. This also makes the empty-batch behavior explicit to callers.

Keep the columns paired

Two independent IN lists are not equivalent. A condition such as ticket_id IN (...) AND status IN (...) allows every combination of the listed IDs and statuses, including pairs the caller never requested. A joined values table matches only the supplied rows.

PostgreSQL also supports row-constructor comparisons such as (ticket_id, status) IN ((101, 'open'), ...). Its row comparison documentation explains equality and null behavior. The values-table form is usually easier to generate safely for an arbitrary batch.

Preserve parameterization and useful indexes

Postgres.js tagged templates separate data values from the query text. Its dynamic values documentation shows how arrays are escaped and how multiple rows become a VALUES list. Do not wrap interpolations in quotes or build tuple strings manually.

A composite index on the same columns can support this lookup when the table and batch are large. Choose the leading column according to broader query patterns rather than this example alone. Continue with SQL IN conditions, composite keys, and SQL joins.

Related Web Cheat Sheet guides

Sergey Kornilov

Sergey Kornilov