2 votes

Insertions massives avec pg-promise

J'utilise pg-promesse et je veux faire des insertions multiples dans une table. J'ai vu des solutions comme Insert multi-rangs avec pg-promise y Comment insérer correctement plusieurs lignes dans PG avec node-postgres ? et je pourrais utiliser pgp.helpers.concat pour concaténer plusieurs sélections.

Mais maintenant, j'ai besoin d'insérer un grand nombre de mesures dans une table, avec plus de 10 000 enregistrements, et en https://github.com/vitaly-t/pg-promise/wiki/Performance-Boost dit : " Le nombre d'enregistrements que vous pouvez concaténer de cette manière - dépend de la taille des enregistrements, mais je ne dépasserais jamais les 10 000 enregistrements avec cette approche ". Donc, si vous devez insérer beaucoup plus d'enregistrements, vous voudrez les diviser en lots concaténés de ce type, puis les exécuter un par un."

J'ai lu tout l'article mais je n'arrive pas à comprendre comment "diviser" mes insertions en lots puis les exécuter une par une.

Gracias.

1voto

AlexB Points 2135

Je pense que l'approche naïve fonctionnerait.

Essayez de diviser vos données en plusieurs morceaux de 10 000 enregistrements ou moins. J'essaierais de diviser le tableau en utilisant la solution suivante poste .

Ensuite, on insère plusieurs rangs dans chaque tableau avec pg-promesse et les exécuter un par un dans une transaction.

Edit : Merci à @vitaly-t pour la magnifique bibliothèque et pour avoir amélioré ma réponse. .

N'oubliez pas non plus d'inclure vos requêtes dans une transaction, sinon il épuisera les connexions.

Pour ce faire, utilisez l'option lot de pg-promise pour résoudre toutes les requêtes de manière asynchrone :

// split your array here to get splittedData
int i = 0 
var cs = new pgp.helpers.ColumnSet(['col_a', 'col_b'], {table: 'tmp'})

// values = [..,[{col_a: 'a1', col_b: 'b1'}, {col_a: 'a2', col_b: 'b2'}]]
let queries = []
for (var i = 0; i < splittedData.length; i++) {
   var query = pgp.helpers.insert(splittedData[i], cs)
   queries.push(query)
}

db.tx(function () {
   this.batch(queries)
})
.then(function (data) {
   // all record inserted successfully ! 
}
.catch(function (error) {
    // error;
});

1voto

user1102051 Points 152

UPDATE

Le mieux est de lire l'article suivant : Importations de données .


En tant qu'auteur de pg-promesse Je me suis senti obligé de fournir enfin la bonne réponse à la question, car celle publiée précédemment ne lui rendait pas vraiment justice.

Afin d'insérer un nombre massif/infini d'enregistrements, votre approche doit se baser sur la méthode séquence qui est disponible dans les tâches et les transactions.

var cs = new pgp.helpers.ColumnSet(['col_a', 'col_b'], {table: 'tableName'});

// returns a promise with the next array of data objects,
// while there is data, or an empty array when no more data left
function getData(index) {
    if (/*still have data for the index*/) {
        // - resolve with the next array of data
    } else {
        // - resolve with an empty array, if no more data left
        // - reject, if something went wrong
    }        
}

function source(index) {
    var t = this;
    return getData(index)
        .then(data => {
            if (data.length) {
                // while there is still data, insert the next bunch:
                var insert = pgp.helpers.insert(data, cs);
                return t.none(insert);
            }
            // returning nothing/undefined ends the sequence
        });
}

db.tx(t => t.sequence(source))
    .then(data => {
        // success
    })
    .catch(error => {
        // error
    });

Il s'agit de la meilleure approche pour insérer un nombre massif de lignes dans la base de données, tant du point de vue des performances que de la limitation de la charge.

Tout ce que vous avez à faire est d'implémenter votre fonction getData en fonction de la logique de votre application, c'est-à-dire de l'endroit d'où proviennent vos données volumineuses, en fonction de la index de la séquence, pour renvoyer quelque 1 000 à 10 000 objets à la fois, en fonction de la taille des objets et de la disponibilité des données.

Voir aussi quelques exemples d'API :


Question connexe : node-postgres avec une quantité massive de requêtes .


Et dans les cas où vous avez besoin d'acquérir les identifiants générés de tous les enregistrements insérés, vous devez modifier les deux lignes comme suit :

// return t.none(insert);
return t.map(insert + 'RETURNING id', [], a => +a.id);

y

// db.tx(t => t.sequence(source))
db.tx(t => t.sequence(source, {track: true}))

mais faites attention, car garder trop d'identifiants d'enregistrement en mémoire peut créer une surcharge.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X