Как правильно вставить несколько строк в PG с помощью node-postgres?

Одна строка может быть вставлена ​​так:

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 для компиляции таких строк вручную, но затем мне нужно как-то добавить специальные символы escape.

8 ответов

Решение

После этой статьи: Повышение производительности из библиотеки pg-promise и предлагаемый подход:

// 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
    });

ОБНОВИТЬ

Для высокопроизводительного подхода через один INSERT запрос см. Многострочная вставка с pg-обещанием.

Используйте pg-формат npm, как показано ниже.

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);
});

Еще один способ использования json-функций PostgreSQL:

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);
      }
});
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-инъекции, если вы сделаете это неправильно. Также легко получить ошибку на единицу между индексом ваших параметров в запросе и параметрами, которые вы передаете.

При этом, вот пример того, как вы могли бы написать это, предполагая, что у вас есть массив пользователей, который выглядит как :

      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 требует, чтобы запрос был помечен и использует это, чтобы гарантировать, что любые передаваемые вами пользовательские данные всегда автоматически экранируются. Это также позволяет вам записывать параметры в строку, что, как мне кажется, делает код более читабельным.

Простое использование встроенного параметризованного запроса модуля pg со сглаженными значениями. Подумал, что это может быть то, что некоторые ищут. Нижеследующее сделано для удобства.

      function makeValuesString(columnCount, rowCount) {
  return Array.from({ length: rowCount }, (_, i) => `(${Array.from({ length: columnCount }, (_, j) => `$${i * columnCount + j + 1}`).join(', ')})`).join(', ')
}

const columns = ['id', 'column1', 'column2', 'column3'] // columnNames
const query = {
  text: `INSERT INTO tableName (${columns.join(', ')})
  VALUES ${makeValuesString(columns.length, rows.length)}`,
  values: rows.reduce((acc, cv) => {
    acc.push(...cv) // rowValues
    return acc
  }, [])
}

const result = await client.query(query)

Использование модуля npm postgres (porsager/postgres), в основе которого лежат тегированные строки шаблона:

https://github.com/porsager/postgres#multiple-inserts-in-one-query

      const users = [{
  name: 'Murray',
  age: 68,
  garbage: 'ignore'
},
{
  name: 'Walter',
  age: 80,
  garbage: 'ignore'
}]

sql`insert into users ${ sql(users, 'name', 'age') }`

// Is translated to:
insert into users ("name", "age") values ($1, $2), ($3, $4)

// Here you can also omit column names which will use all object keys as columns
sql`insert into users ${ sql(users) }`

// Which results in:
insert into users ("name", "age", "garbage") values ($1, $2, $3), ($4, $5, $6)

Просто подумал, что опубликую, так как это как новинка из бета-версии, и я обнаружил, что это лучшая философия библиотеки SQL. Я думаю, что это предпочтительнее других библиотек postgres/node, опубликованных в других ответах. ИМХО

Привет, я знаю, что опаздываю на вечеринку, но мне помогла простая карта.

Я надеюсь, что это поможет кому-то, кто ищет то же самое

Другие вопросы по тегам