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

如何在TypeORM的whereIn语句中使用多列查询?PostgreSQL环境

如何在TypeORM中实现PostgreSQL的复合字段IN查询?

问题背景

我定义了如下TypeORM实体:

@Entity({ name: 'test_table' })
export class SomeEntity {
  @PrimaryGeneratedColumn({ type: 'int4', name: 'id' })
  id: number;
  @Column({ type: 'int2', name: 'some_column_1' })
  someColumn1: number;
  @Column({ type: 'text', name: 'some_column_2' })
  someColumn2: string;
}

想要执行这样的PostgreSQL查询:

SELECT id, some_column_1, some_column_2 FROM test_table WHERE (some_column_1, some_column_2) IN ((1, 'test1'), (2, 'test2'))

尝试了以下代码却报错:

// 省略部分代码
.where("(some_column_1, some_column_2) IN (:...arr)", { arr: [ [1, 'test1'], [2, 'test2'] ] })
.getMany();

错误信息:Error: input of anonymous composite types is not implemented

解决方案

方法1:原生VALUES子句+参数绑定

这是最贴近你目标SQL的实现方式,通过构造VALUES子句来规避匿名复合类型的问题:

const targetPairs = [[1, 'test1'], [2, 'test2']];
// 生成参数占位符,比如 ($1, $2), ($3, $4)
const placeholders = targetPairs.map((_, idx) => `($${idx*2 + 1}, $${idx*2 + 2})`).join(', ');
// 把二维数组扁平化,方便参数绑定
const flatParams = targetPairs.flat();

const result = await getRepository(SomeEntity)
  .createQueryBuilder('t')
  .where(`(t.some_column_1, t.some_column_2) IN (${placeholders})`)
  .setParameters(flatParams)
  .getMany();

这种方式既保留了你想要的IN子句结构,又通过参数绑定避免了SQL注入风险,同时解决了PostgreSQL的复合类型识别问题。

方法2:OR条件组合(适合少量匹配项)

如果你的匹配对数量不多,用TypeORM的QueryBuilder来组合多个AND+OR条件也是个不错的选择:

const matchConditions = [
  { someColumn1: 1, someColumn2: 'test1' },
  { someColumn1: 2, someColumn2: 'test2' }
];

const queryBuilder = getRepository(SomeEntity).createQueryBuilder('t');

matchConditions.forEach((cond, index) => {
  const paramPrefix = `pair${index}`;
  const condition = `t.some_column_1 = :${paramPrefix}_col1 AND t.some_column_2 = :${paramPrefix}_col2`;
  
  if (index === 0) {
    queryBuilder.where(condition, {
      [`${paramPrefix}_col1`]: cond.someColumn1,
      [`${paramPrefix}_col2`]: cond.someColumn2
    });
  } else {
    queryBuilder.orWhere(condition, {
      [`${paramPrefix}_col1`]: cond.someColumn1,
      [`${paramPrefix}_col2`]: cond.someColumn2
    });
  }
});

const result = await queryBuilder.getMany();

这种写法更符合TypeORM的原生风格,不需要手写大量原生SQL,只是当匹配对很多时,生成的SQL会比较冗长。

方法3:利用PostgreSQL的unnest函数转换数组

通过将二维数组拆分成两个一维数组,再用unnest转换为行数据来匹配:

const targetPairs = [[1, 'test1'], [2, 'test2']];

const result = await getRepository(SomeEntity)
  .createQueryBuilder('t')
  .where(`(t.some_column_1, t.some_column_2) IN (SELECT unnest(:col1Vals), unnest(:col2Vals))`, {
    col1Vals: targetPairs.map(pair => pair[0]),
    col2Vals: targetPairs.map(pair => pair[1])
  })
  .getMany();

这种方式也能解决问题,不过需要注意unnest的顺序要严格对应,确保两个数组的元素位置匹配。

原方法报错原因

你之前的写法之所以出错,是因为TypeORM会把传入的二维数组参数转换成PostgreSQL的数组类型,而(col1, col2) IN (...)需要的是行的集合,不是数组类型。PostgreSQL无法直接识别这种匿名复合类型的输入,所以抛出了那个错误。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.07 11:17:36