【问题标题】:How do I properly insert multiple rows into PG with node-postgres?如何使用 node-postgres 将多行正确插入 PG?
【发布时间】:2016-05-01 14:52:36
【问题描述】:

可以像这样插入一行:

client.query("insert into tableName (name, email) values ($1, $2) ", ['john', 'john@gmail.com'], callBack)

这种方法会自动排除任何特殊字符。

如何一次插入多行?

我需要实现这个:

"insert into tableName (name, email) values ('john', 'john@gmail.com'), ('jane', 'jane@gmail.com')"

我可以使用 js 字符串运算符手动编译这些行,但是我需要以某种方式添加特殊字符转义。

【问题讨论】:

  • 您是否有理由不能简单地执行两次 INSERT?
  • 关于 pg 文档,由于性能下降,这种方法是非常不可取的
  • 如果执行 2 次插入而不是 1 次会危及应用程序的性能,那么 node-postgres 根本不适合您。但我相信你以错误的方式看待它,试图优化你不应该的地方。该库可以在 1 秒内轻松插入 10,000 条记录。
  • 您的情况是否令人满意?如果是,请接受。

标签: javascript sql node.js postgresql node-postgres


【解决方案1】:

使用pg-format,如下所示。

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

var values = [
  [7, 'john22', 'john22@gmail.com', '9999999922'], 
  [6, 'testvk', 'testvk@gmail.com', '88888888888']
];
client.query(format('INSERT INTO users (id, name, email, phone) VALUES %L', values),[], (err, result)=>{
  console.log(err);
  console.log(result);
});

【讨论】:

    【解决方案2】:

    另一种使用 PostgreSQL json 函数的方式:

    client.query('INSERT INTO table (columns) ' +
      'SELECT m.* FROM json_populate_recordset(null::your_custom_type, $1) AS m',
      [JSON.stringify(your_json_object_array)], function(err, result) {
        if (err) {
          console.log(err);
        } else {
          console.log(result);
        }
    });
    

    【讨论】:

    • 您可能会使用NULL::table 作为类型,但您需要SELECTINSERT 中列出的相同的各个列。
    • 您不必为调用json_populate_recordset() 函数定义自定义类型,而是可以指定要插入的表的名称作为类型。例如,当插入一个名为 foo 的表时,函数调用可能如下所示:json_populate_recordset(null::foo)
    • @Neoheurist 在json结构对应于foo的情况下你是对的。如果json结构不同,则需要定义you_custom_type。
    【解决方案3】:

    您将不得不动态生成查询。虽然有可能,但这是有风险的,如果你做错了,很容易导致 SQL 注入漏洞。查询中的参数索引和传入的参数之间也很容易出现一个错误。

    话虽如此,下面是一个示例,说明如何编写此代码,假设您有一组看起来像 {name: string, email: string} 的用户:

    client.query(
      `INSERT INTO table_name (name, email) VALUES ${users.map(() => `(?, ?)`).join(',')}`,
      users.reduce((params, u) => params.concat([u.name, u.email]), []),
      callBack,
    )
    

    另一种方法是使用像@databases/pg(我写的)这样的库:

    await db.query(sql`
      INSERT INTO table_name (name, email)
      VALUES ${sql.join(users.map(u => sql`(${u.name}, ${u.email})`), ',')}
    `)
    

    @databases 要求使用sql 标记查询,并使用它来确保您传递的任何用户数据始终自动转义。这也让您可以内联编写参数,我认为这使代码更具可读性。

    【讨论】:

      【解决方案4】:

      按照这篇文章:来自pg-promise 库的Performance Boost 及其建议的方法:

      // Concatenates an array of objects or arrays of values, according to the template,
      // to use with insert queries. Can be used either as a class type or as a function.
      //
      // template = formatting template string
      // data = array of either objects or arrays of values
      function Inserts(template, data) {
          if (!(this instanceof Inserts)) {
              return new Inserts(template, data);
          }
          this._rawDBType = true;
          this.formatDBType = function () {
              return data.map(d=>'(' + pgp.as.format(template, d) + ')').join(',');
          };
      }
      

      一个使用它的例子,就像你的情况一样:

      var users = [['John', 23], ['Mike', 30], ['David', 18]];
      
      db.none('INSERT INTO Users(name, age) VALUES $1', Inserts('$1, $2', users))
          .then(data=> {
              // OK, all records have been inserted
          })
          .catch(error=> {
              // Error, no records inserted
          });
      

      它也适用于对象数组:

      var users = [{name: 'John', age: 23}, {name: 'Mike', age: 30}, {name: 'David', age: 18}];
      
      db.none('INSERT INTO Users(name, age) VALUES $1', Inserts('${name}, ${age}', users))
          .then(data=> {
              // OK, all records have been inserted
          })
          .catch(error=> {
              // Error, no records inserted
          });
      

      UPDATE-1

      有关通过单个 INSERT 查询的高性能方法,请参阅 Multi-row insert with pg-promise

      UPDATE-2

      这里的信息现在已经很老了,请参阅Custom Type Formatting 的最新语法。以前的_rawDBType 现在是rawTypeformatDBType 更名为toPostgres

      【讨论】:

      • 问题是关于 node-postgress API。您对 pg-promise 的回答是否涵盖了它?
      • @AlexeySh.pg-promise 派生自 node-postgres,所以从某种意义上说,是的。
      • 如果你在这里推荐你自己的图书馆会很好:)
      【解决方案5】:
      client.query("insert into tableName (name, email) values ($1, $2),($3, $4) ", ['john', 'john@gmail.com','john', 'john@gmail.com'], callBack)
      

      没有帮助吗? 此外,您可以手动生成一个字符串进行查询:

      insert into tableName (name, email) values (" +var1 + "," + var2 + "),(" +var3 + ", " +var4+ ") "
      

      如果您在这里阅读 https://github.com/brianc/node-postgres/issues/530 ,您可以看到相同的实现。

      【讨论】:

      • 这适用于少量插入,但对于数百个插入,您需要循环中的某些内容。此外,您的第二个建议容易受到 SQL 注入的影响。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2017-07-19
      • 2021-04-07
      • 2017-07-17
      • 2019-01-19
      • 1970-01-01
      相关资源
      最近更新 更多