【问题标题】:Multi-row insert with pg-promise使用 pg-promise 进行多行插入
【发布时间】:2016-09-15 00:31:33
【问题描述】:

我想用一个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 注入?

【问题讨论】:

    标签: javascript node.js postgresql pg-promise


    【解决方案1】:

    我是pg-promise的作者。

    在旧版本的库中,Performance Boost 文章中的简化示例涵盖了这一点,在编写高性能数据库应用程序时仍然值得一读。

    较新的方法是依赖helpers namespace,它最终是灵活的,并且针对性能进行了优化。

    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 注入。


    相关问题:PostgreSQL multi-row updates in Node.js


    附加

    Q:如何同时获取每条新记录的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 returns integers as strings

    UPDATE-1

    要插入大量记录,请参阅Data Imports

    UPDATE-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(或其他信息)?您在Inserting multiple records with pg-promise 中为批处理查询提出了类似的建议。
    • @IvanSchwarz 当然,只需将 `RETURNING id` 附加到生成的查询中,并使用 many 而不是 none 执行它。
    • @vitaly-t 是否可以实现批量 upsert 助手?基本上是这样的:插入表(col1,col2)值('a1','b1'),('a2','b2')ON CONFLICT(col1)DO UPDATE SET(col1,col2)=(' a1','b1'),('a2','b2')
    • 记得将所有列设为小写,否则可能会遇到错误。
    • @sayreskabir 是的。你可以使用Column中的属性init,并设置mod: ':raw',或者使用Custom Type Formattinghelpers也尊重。
    【解决方案2】:

    试试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 方法不具备上述任何一种。它既不灵活也不可扩展。这甚至忽略了答案依赖于同一个查询库,而这带来了一个额外的库。
    猜你喜欢
    • 2020-11-29
    • 2019-09-27
    • 2016-07-14
    • 2019-11-12
    • 1970-01-01
    • 1970-01-01
    • 2016-12-06
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多