Comment faire une insertion en masse dans mySQL en utilisant node.js

123

Comment faire une insertion en bloc dans mySQL si vous utilisez quelque chose comme https://github.com/felixge/node-mysql

crickeys
la source
Quel est votre problème? Pouvez-vous le faire de la même manière qu'avec une commande SQL? Commencez simplement la commande suivante à la fin de la précédente jusqu'à ce que vous ayez inséré toutes les données.
Andrey Sidorov
3
J'avais l'impression que les inserts BULK sont plus rapides que de nombreux inserts simples.
crickeys
au niveau du fil, ils sont pareils. Il n'y a pas de `` insertion en bloc '' dans le protocole mysql
Andrey Sidorov
1
il y a insertion multiple dans mySQL, vous utilisez simplement le mot clé VALUES. dev.mysql.com/doc/refman/5.5/en/insert.html Les instructions INSERT qui utilisent la syntaxe VALUES peuvent insérer plusieurs lignes. Pour ce faire, incluez plusieurs listes de valeurs de colonne, chacune placée entre parenthèses et séparée par des virgules. Exemple: INSERT INTO tbl_name (a, b, c) VALUES (1,2,3), (4,5,6), (7,8,9);
crickeys

Réponses:

288

Les insertions en masse sont possibles en utilisant un tableau imbriqué, voir la page github

Les tableaux imbriqués sont transformés en listes groupées (pour les insertions groupées), par exemple [['a', 'b'], ['c', 'd']]se transforment en('a', 'b'), ('c', 'd')

Vous insérez simplement un tableau imbriqué d'éléments.

Un exemple est donné ici

var mysql = require('mysql');
var conn = mysql.createConnection({
    ...
});

var sql = "INSERT INTO Test (name, email, n) VALUES ?";
var values = [
    ['demian', '[email protected]', 1],
    ['john', '[email protected]', 2],
    ['mark', '[email protected]', 3],
    ['pete', '[email protected]', 4]
];
conn.query(sql, [values], function(err) {
    if (err) throw err;
    conn.end();
});

Remarque: valuesest un tableau de tableaux enveloppés dans un tableau

[ [ [...], [...], [...] ] ]

Il existe également un package node-msql totalement différent pour l'insertion en masse

Ragnar123
la source
2
Cela offre-t-il les mêmes protections que l' conn.execute()utilisation de déclarations préparées? Sinon, est-il possible d'utiliser des instructions préparées lors de l'insertion? Merci.
Vigs
2
Oui, les valeurs sont échappées avec cette méthode. Je pense que c'est le même mécanisme que les déclarations préparées, qui utilise également connection.escape () en interne.
Ragnar123
7
Cela me déroute. Pourquoi le tableau doit-il être [[['a', 'b'], ['c', 'd']]] et non [['a', 'b'], ['c', 'd ']] comme le dit la documentation?
Victorio Berra
12
Victorio Berra, c'est parce que le tableau le plus externe est celui qui correspond aux points d'interrogation dans l'instruction en général, pas seulement dans l'insertion. Par exemple, si vous aviez deux espaces réservés de point d'interrogation, vous auriez [param1, param2]. Dites "UPDATE Users? WHERE ID =?", [Columns, ID] Ainsi, les colonnes seront développées jusqu'au premier point d'interrogation et l'ID au second.
Selay
3
Malheureusement, cette réponse ne fonctionne pas pour moi. J'ai littéralement copié votre réponse mais sans succès. J'ai posté une autre question sur stackoverflow.com/questions/41170849/…
Ivan Pandžić
19

La réponse @ Ragnar123 est correcte, mais je vois beaucoup de gens dire dans les commentaires que cela ne fonctionne pas. J'ai eu le même problème et il semble que vous deviez envelopper votre tableau []comme ceci:

var pars = [
    [99, "1984-11-20", 1.1, 2.2, 200], 
    [98, "1984-11-20", 1.1, 2.2, 200], 
    [97, "1984-11-20", 1.1, 2.2, 200]
];

Il doit être passé comme [pars]dans la méthode.

Codendaal
la source
6
Ouais, pour une raison quelconque, il doit s'agir d'un tableau, d'un tableau de tableaux ...
Joe
On dirait également que cela nécessite un singulier ?au lieu de ??pour obtenir le bon groupement.
Federico le
15

Je cherchais une réponse sur l'insertion d'objets en masse.

La réponse de Ragnar123 m'a amené à créer cette fonction:

function bulkInsert(connection, table, objectArray, callback) {
  let keys = Object.keys(objectArray[0]);
  let values = objectArray.map( obj => keys.map( key => obj[key]));
  let sql = 'INSERT INTO ' + table + ' (' + keys.join(',') + ') VALUES ?';
  connection.query(sql, [values], function (error, results, fields) {
    if (error) callback(error);
    callback(null, results);
  });
}

bulkInsert(connection, 'my_table_of_objects', objectArray, (error, response) => {
  if (error) res.send(error);
  res.json(response);
});

J'espère que ça aide!

Stephen Gibson
la source
11

J'ai rencontré ceci aujourd'hui ( mysql 2.16.0) et j'ai pensé partager ma solution:

const items = [
    {name: 'alpha', description: 'describes alpha', value: 1},
    ...
];

db.query(
    'INSERT INTO my_table (name, description, value) VALUES ?',
    [items.map(item => [item.name, item.description, item.value])],
    (error, results) => {...}
);
SnobOfTheGolfClub
la source
3
J'aime votre solution
Joe
Cette solution a fonctionné pour moi! TY! POSTMAN: [{"textQuestionBuilderID": "5", "candidateID": "ABC123", "resultSelected": "sfgh"}, {"textQuestionBuilderID": "6", "candidateID": "ABC123", "resultSelected": "sfgh"}, {"textQuestionBuilderID": "7", "candidateID": "ABC123", "resultSelected": "sfgh"}, {"textQuestionBuilderID": "8", "candidateID": "ABC123", "resultSelected ":" sfgh "}]
Brian
8

Tous les accessoires à Ragnar123 pour sa réponse.

Je voulais juste l'élargir après la question posée par Josh Harington pour parler des identifiants insérés.

Ceux-ci seront séquentiels. Voir cette réponse: Est-ce qu'une insertion multi-lignes MySQL saisit les ID d'auto-incrémentation séquentielle?

Par conséquent, vous pouvez simplement faire ceci (remarquez ce que j'ai fait avec le result.insertId):

  var statement = 'INSERT INTO ?? (' + sKeys.join() + ') VALUES ?';
  var insertStatement = [tableName, values];
  var sql = db.connection.format(statement, insertStatement);
  db.connection.query(sql, function(err, result) {
    if (err) {
      return clb(err);
    }
    var rowIds = [];
    for (var i = result.insertId; i < result.insertId + result.affectedRows; i++) {
      rowIds.push(i);
    }
    for (var i in persistentObjects) {
      var persistentObject = persistentObjects[i];
      persistentObject[persistentObject.idAttributeName()] = rowIds[i];
    }
    clb(null, persistentObjects);
  });

(J'ai extrait les valeurs d'un tableau d'objets que j'ai appelé persistentObjects.)

J'espère que cela t'aides.

thewormsterror
la source
Sommes-nous garantis que dans le cas d'inserts simultanés, la condition de course ne mélangera pas les ID d'insert?
Purefan
1
@Purefan D'après mes tests, oui, mais qui sait si cela changera un jour.
thewormsterror
Notez que cela ne fonctionnera que si la taille de l'étape auto_increment est sur sa valeur d'origine de 1. Voir dev.mysql.com/doc/refman/5.7/en
...
6

Ceci est un "raw-copy-paste" rapide extrait pour pousser une colonne de fichier dans mysql avec node.js> = 11

250 000 lignes en quelques secondes

'use strict';

const mysql = require('promise-mysql');
const fs = require('fs');
const readline = require('readline');

async function run() {
  const connection = await mysql.createConnection({
    host: '1.2.3.4',
    port: 3306,
    user: 'my-user',
    password: 'my-psw',
    database: 'my-db',
  });

  const rl = readline.createInterface({ input: fs.createReadStream('myfile.txt') });

  let total = 0;
  let buff = [];
  for await (const line of rl) {
    buff.push([line]);
    total++;
    if (buff.length % 2000 === 0) {
      await connection.query('INSERT INTO Phone (Number) VALUES ?', [buff]);
      console.log(total);
      buff = [];
    }
  }

  if (buff.length > 0) {
    await connection.query('INSERT INTO Phone (Number) VALUES ?', [buff]);
    console.log(total);
  }

  console.log('end');
  connection.close();
}

run().catch(console.log);
Manuel Spigolon
la source
1
Cela fonctionne incroyablement bien, merci! Note aux adaptateurs: même si vous avez plusieurs colonnes dans INSERT, la clé est de garder cette seule ?après VALUES, sans crochets. De cette façon, un tableau de tableaux de colonnes peut être automatiquement traité dans des insertions en masse. Je l'ai utilisé pour analyser des centaines de mégaoctets de journaux d'accès dans MySQL.
Alex Pakka
Je n'avais pas réalisé pourquoi vous divisiez cela en morceaux avant de l'essayer sans. À un moment donné, mon insert est devenu trop gros pour que le serveur puisse le traiter en temps opportun et j'ai eu une erreur EPIPE. La division de l'insert en morceaux résout ce problème. :)
Kit Peters
4

Au cas où cela serait nécessaire, voici comment nous avons résolu l'insertion du tableau

la demande provient du facteur (vous regarderez "invités")

 {
  "author_id" : 3,
  "name" : "World War II",
  "date" : "01 09 1939", 
  "time" : "16 : 22",
  "location" : "39.9333635/32.8597419",
  "guests" : [2, 3, 1337, 1942, 1453]
}

Et comment nous avons scénarisé

var express = require('express');
var utils = require('./custom_utils.js');

module.exports = function(database){
    var router = express.Router();

    router.post('/', function(req, res, next) {
        database.query('INSERT INTO activity (author_id, name, date, time, location) VALUES (?, ?, ?, ?, ?) ON DUPLICATE KEY UPDATE name = VALUES(name), date = VALUES(date), time = VALUES(time), location = VALUES(location)', 
                [req.body.author_id, req.body.name, req.body.date, req.body.time, req.body.location], function(err, results, fields){
            if(err){
                console.log(err);
                res.json({ status: utils.respondMSG.DB_ERROR });
            }
            else {
                var act_id = results.insertId;
                database.query('INSERT INTO act_guest (user_id, activity_id, status) VALUES ? ON DUPLICATE KEY UPDATE status = VALUES(status)', 
                        [Array.from(req.body.guests).map(function(g){ return [g, act_id, 0]; })], function(err, results, fields){
                    if(err){
                        console.log(err);
                        res.json({ status: utils.respondMSG.DB_ERROR });
                    }
                    else {
                        res.json({ 
                            status: utils.respondMSG.SUCCEED,
                            data: {
                                activity_id : act_id
                            }
                        });
                    }
                });
            }
        });
    });
    return router;
};
Sam
la source
2

RagnarLa réponse de If ne fonctionne pas pour vous. Voici probablement pourquoi (d'après mon expérience) -

  1. Je n'utilisais pas le node-mysqlpackage comme indiqué mon Ragnar. J'utilisais mysqlpackage. Ils sont différents (si vous ne l'avez pas remarqué - tout comme moi). Mais je ne sais pas si cela a quelque chose à voir avec le ?non fonctionnement, car cela semblait fonctionner pour de nombreuses personnes utilisant le mysqlpackage.

  2. Essayez d'utiliser une variable au lieu de ?

Ce qui suit a fonctionné pour moi -

var mysql = require('node-mysql');
var conn = mysql.createConnection({
    ...
});

var sql = "INSERT INTO Test (name, email, n) VALUES :params";
var values = [
    ['demian', '[email protected]', 1],
    ['john', '[email protected]', 2],
    ['mark', '[email protected]', 3],
    ['pete', '[email protected]', 4]
];
conn.query(sql, { params: values}, function(err) {
    if (err) throw err;
    conn.end();
});

J'espère que cela aide quelqu'un.

Aswin Ramakrishnan
la source
Je pense que vous pourriez oublier de mettre le [] à des valeurs. Ça m'est aussi arrivé. Cela devrait être: conn.query (sql, [values], function () {}) Au lieu de: conn.query (sql, values, function () {}) Malgré cela, la variable values ​​est un tableau, mais nous avons toujours pour l'envelopper avec []
Anh Nguyen
Le package node-mysql actuel a une syntaxe totalement différente de celle du package mysql. Le lien du package node-mysql est npmjs.com/package/node-mysql Le lien du package mysql est github.com/mysqljs/mysql#escaping-query-values
Agnel Vishal
2

Peu de choses que je veux mentionner, c'est que j'utilise le package mysql pour établir une connexion avec ma base de données et ce que vous avez vu ci-dessous est du code de travail et écrit pour insérer une requête en masse.

const values = [
  [1, 'DEBUG', 'Something went wrong. I have to debug this.'],
  [2, 'INFO', 'This just information to end user.'],
  [3, 'WARNING', 'Warning are really helping users.'],
  [4, 'SUCCESS', 'If everything works then your request is successful']
];

const query = "INSERT INTO logs(id, type, desc) VALUES ?";

const query = connection.query(query, [values], function(err, result) {
  if (err) {
    console.log('err', err)
  }

  console.log('result', result)
});
Sagar
la source
1

J'avais un problème similaire. C'était juste en insérant un de la liste des tableaux. Cela a fonctionné après avoir apporté les modifications ci-dessous.

  1. Passé [params] à la méthode de requête.
  2. Modification de la requête d'insertion (a, b) en valeurs table1 (?) ==> insert (a, b) en valeurs table1? . c'est à dire. Suppression de la paranthèse autour du point d'interrogation.

J'espère que cela t'aides. J'utilise mysql npm.

user1998289
la source
0

L'insertion en bloc dans Node.js peut être effectuée à l'aide du code ci-dessous. J'ai référé beaucoup de blog pour obtenir ce travail.

veuillez également consulter ce lien. https://www.technicalkeeda.com/nodejs-tutorials/insert-multiple-records-into-mysql-using-nodejs

Le code de travail.

  const educations = request.body.educations;
  let queryParams = [];
  for (let i = 0; i < educations.length; i++) {
    const education = educations[i];
    const userId = education.user_id;
    const from = education.from;
    const to = education.to;
    const instituteName = education.institute_name;
    const city = education.city;
    const country = education.country;
    const certificateType = education.certificate_type;
    const studyField = education.study_field;
    const duration = education.duration;

    let param = [
      from,
      to,
      instituteName,
      city,
      country,
      certificateType,
      studyField,
      duration,
      userId,
    ];

    queryParams.push(param);
  }

  let sql =
    "insert into tbl_name (education_from, education_to, education_institute_name, education_city, education_country, education_certificate_type, education_study_field, education_duration, user_id) VALUES ?";
  let sqlQuery = dbManager.query(sql, [queryParams], function (
    err,
    results,
    fields
  ) {
    let res;
    if (err) {
      console.log(err);
      res = {
        success: false,
        message: "Insertion failed!",
      };
    } else {
      res = {
        success: true,
        id: results.insertId,
        message: "Successfully inserted",
      };
    }

    response.send(res);
  });

J'espère que ceci vous aidera.

Yadhu
la source