带有 pg-promise 的多行插入

IT技术 javascript node.js postgresql pg-promise
2021-01-18 20:10:39

我想用一个INSERT查询插入多行,例如:

INSERT INTO tmp(col_a,col_b) VALUES('a1','b1'),('a2','b2')...

有没有办法轻松地做到这一点,最好是像这样的对象数组:

[{col_a:'a1',col_b:'b1'},{col_a:'a2',col_b:'b2'}]

我可能会在一个块中得到 500 条记录,因此运行多个查询是不可取的。

到目前为止,我只能为单个对象执行此操作:

INSERT INTO tmp(col_a,col_b) VALUES(${col_a},${col_b})

作为一个附带问题:使用${}符号的插入是否可以防止 SQL 注入?

2个回答

我是pg-promise的作者

在旧版本的库中,性能提升文章中的简化示例涵盖了这一点,在编写高性能数据库应用程序时,这仍然是一个很好的阅读。

较新的方法是依赖于helpers 命名空间,它最终是灵活的,并针对性能进行了优化。

const pgp = require('pg-promise')({
    /* initialization options */
    capSQL: true // capitalize all generated SQL
});
const db = pgp(/*connection*/);
    
// our set of columns, to be created only once (statically), and then reused,
// to let it cache up its formatting templates for high performance:
const cs = new pgp.helpers.ColumnSet(['col_a', 'col_b'], {table: 'tmp'});
    
// data input values:
const values = [{col_a: 'a1', col_b: 'b1'}, {col_a: 'a2', col_b: 'b2'}];
    
// generating a multi-row insert query:
const query = pgp.helpers.insert(values, cs);
//=> INSERT INTO "tmp"("col_a","col_b") VALUES('a1','b1'),('a2','b2')
    
// executing the query:
await db.none(query);

请参阅 API:ColumnSetinsert

这种插入甚至不需要事务,因为如果一组值插入失败,则不会插入任何值。

您可以使用相同的方法生成以下任何查询:

  • 单排 INSERT
  • 多行 INSERT
  • 单排 UPDATE
  • 多行 UPDATE

使用 ${} 符号的插入是否可以防止 sql 注入?

是的,但并不孤单。如果您要动态插入模式/表/列名称,请务必使用SQL Names,这将保护您的代码免受 SQL 注入。


相关问题:Node.js 中的 PostgreSQL 多行更新


额外的

问:如何同时获取id每条新记录?

答:只需附加RETURNING id到您的查询,并使用方法many执行它

const query = pgp.helpers.insert(values, cs) + ' RETURNING id';
    
const res = await db.many(query);
//=> [{id: 1}, {id: 2}, ...]

或者更好的是,获取 id-s,然后使用map方法将结果转换为整数数组

const res = await db.map(query, undefined, a => +a.id);
//=> [1, 2, ...]

要了解我们为什么在那里使用+,请参阅:pg-promise 将整数作为字符串返回

更新-1

要插入大量记录,请参阅数据导入

更新-2

使用 v8.2.1 及更高版本,您可以将静态查询生成包装到一个函数中,因此它可以在查询方法中生成,以在查询生成失败时拒绝:

// generating a multi-row insert query inside a function:
const query = () => pgp.helpers.insert(values, cs);
//=> INSERT INTO "tmp"("col_a","col_b") VALUES('a1','b1'),('a2','b2')
    
// executing the query as a function that generates the query:
await db.none(query);
@vitaly-t:很好的答案,谢谢!有没有办法在同一查询中返回新插入记录的 id(或其他信息)?您在使用 pg-promise 插入多条记录中为批处理查询提出了类似的建议
2021-03-20 20:10:39
@sayreskabir 是的。您可以使用属性init,并设置mod: ':raw'或使用自定义类型格式,其助手也尊重。
2021-03-27 20:10:39
谢谢你的帮助。我只使用 ${} 插入数据,但是使用您的方法甚至不需要。
2021-03-30 20:10:39
请记住将所有列设为小写,否则您可能会遇到错误。
2021-03-30 20:10:39
@IvanSchwarz 当然,只需将` RETURNING id` 附加到您生成的查询,并使用many而不是none.
2021-04-10 20:10:39

尝试https://github.com/datalanche/node-pg-format - 例如

var format = require('pg-format');

var myNestedArray = [['a', 1], ['b', 2]];
var sql = format('INSERT INTO t (name, age) VALUES %L', myNestedArray); 
console.log(sql); // INSERT INTO t (name, age) VALUES ('a', '1'), ('b', '2')

与对象数组类似。

接受的答案允许简单的冲突解决方法,以及多行更新的相同方法。这种pg-format方法不会做任何一个。它既不灵活也不可扩展。这甚至省略了答案依赖于相同的查询库,而这会带来一个额外的库。
2021-03-30 20:10:39