2016-05-18 15 views
16

desidero inserire più righe con un singolo INSERT ricerca, ad esempio:Multi-fila inserto con pg-promessa

INSERT INTO tmp(col_a,col_b) VALUES('a1','b1'),('a2','b2')... 

c'è un modo per farlo facilmente, preferibilmente per un array di oggetti simili questi:

[{col_a:'a1',col_b:'b1'},{col_a:'a2',col_b:'b2'}] 

potrei finire con 500 record in un pezzo, in modo da eseguire più query non sarebbe auspicabile.

Finora sono stato in grado di farlo per un solo oggetto:

INSERT INTO tmp(col_a,col_b) VALUES(${col_a},${col_b}) 

Come una domanda lato: sono gli inserimenti usando ${} notazione protetti contro le iniezioni SQL?

risposta

34

Sono l'autore di pg-promise.

Nelle versioni precedenti della libreria questo era coperto da esempi semplificati all'interno dell'articolo Performance Boost, che è ancora una lettura importante quando si scrivono applicazioni di database ad alte prestazioni.

L'approccio più recente è affidarsi allo helpers namespace, che in ultima analisi è flessibile e altamente ottimizzato per le prestazioni.

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, and then shared/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: 
db.none(query) 
    .then(data => { 
     // success; 
    }) 
    .catch(error => { 
     // error; 
    }); 

See API: ColumnSet, insert.

Un inserto di questo tipo non richiede nemmeno una transazione, perché se un set di valori non viene inserito, nessuno verrà inserito.

Ed è possibile utilizzare lo stesso approccio per generare uno dei seguenti interrogazioni:

  • sola riga INSERT
  • multi-fila INSERT
  • sola riga UPDATE
  • multi-fila UPDATE

Gli inserimenti utilizzano la notazione $ {} protetta da SQL injection?

Sì, ma non da solo. Se si inseriscono dinamicamente nomi schema/tabella/colonna, è importante utilizzare SQL Names, che in combinazione proteggerà il codice dall'iniezione SQL.


questione connessa: PostgreSQL multi-row updates in Node.js


extra

D: Come ottenere id di ogni nuovo record allo stesso tempo?

A: Semplicemente aggiungendo RETURNING id alla tua richiesta, e l'esecuzione con il metodo many:

const query = pgp.helpers.insert(values, cs) + 'RETURNING id'; 

db.many(query) 
    .then(data => { 
     // data = [{id: 1}, {id: 2}, ...] 
    }) 
    .catch(error => { 
     // error; 
    }); 

o, meglio ancora, ottenere l'ID-s, e convertire il risultato in array di interi, usando il metodo map:

db.map(query, [], a => +a.id) 
    .then(data => { 
     // data = [1, 2, ...] 
    }) 
    .catch(error => { 
     // error; 
    }); 

Per comprendere il motivo per cui abbiamo utilizzato + lì, vedi: pg-promise returns integers as strings.

UPDATE-1

Per l'inserimento di enorme numero di record, vedere Data Imports.

UPDATE-2

Uso V8.2.1 e successivamente, è possibile avvolgere il statica query generazione in una funzione, in modo che possa essere generato all'interno del metodo query rifiutare quando la generazione di query fallisce:

// 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: 
db.none(query) 
    .then(data => { 
     // success; 
    }) 
    .catch(error => { 
     // error; 
     // will get here, even if the query generation fails 
    }); 
+0

Grazie per l'assistenza. Io uso solo $ {} per inserire dati, usando comunque il metodo che non è nemmeno necessario. – Bomaz

+0

@Bomaz Ho apportato alcune modifiche, quindi sai che puoi generare aggiornamenti su più righe e altri tipi di query allo stesso modo;) –

+0

Nice miglioramenti. Btw, potresti sapere cosa causa errori ETIMEDOUT? Le ottengo occasionalmente (questo è in un lambda aws che si collega a redshift). La query restituisce questo errore dopo circa 2 minuti e non sono sicuro su come risolverlo ulteriormente. – Bomaz