Knex: Bagaimana cara menjalankan banyak kueri dalam satu kali proses?

Dibuat pada 29 Apr 2014  ·  40Komentar  ·  Sumber: knex/knex

Saya menghadapi masalah bahwa saya ingin menjalankan beberapa kueri yang dipisahkan oleh ';' oleh satu eksekutif, apakah itu mungkin?

Kode pengujian saya yang gagal terlihat seperti:

      var query = '' +
        'UPDATE "records_raw" ' +
        'SET "title" = ? ' +
        'WHERE "id" = ?' +
        ';' +
        'UPDATE "records_raw" ' +
        'SET "title" = ? ' +
        'WHERE "id" = ?' +
        ';';

      var bindings = [
        "x", "1",
        "y", "2"
      ];

      knex.raw(query, bindings).exec(function(err, result) {
        assert.isNotError(err);
      });

Kesalahan:

Error(cannot insert multiple commands into a prepared statement, ...

Apakah ada cara untuk menonaktifkan pernyataan yang disiapkan untuk pertanyaan seperti itu?

feature request

Komentar yang paling membantu

Perubahan apapun ?
Apakah mungkin sekarang membuat multiQuery?
Jika demikian, menggunakan sintaks apa?

Semua 40 komentar

Tidak, Anda harus menjalankannya sebagai dua pernyataan.

Nah, ini sedikit membatasi solusi

Anda dapat mencoba memanggil toString() pada kueri dan menjalankannya, tetapi saya tidak merekomendasikannya.

knex.raw(knex.raw(query, bindings) + '').exec(function(err, result) {
  assert.isNotError(err);
});

Yah, saya akan melihat apa yang harus dilakukan dengan itu, pada dasarnya saya hanya ingin mematikan pernyataan yang disiapkan dan menjaga kemampuan memiliki string dan binding kueri, tetapi saya tidak tahu apakah itu mungkin, saya tidak menemukan dokumentasi yang terkait dengan ini.

Bagaimanapun, saya mencoba

knex.raw(query, bindings) + ''

tetapi melempar - Objek objek tidak memiliki metode 'kloning'.

Jika Anda ingin mencoba cabang 0.6.0, saya tahu itu akan berhasil di sana, yang harus dirilis setelah saya menyelesaikan tes untuk beberapa hal.

Namun, mengapa Anda perlu menjalankan dua pernyataan kueri menggunakan string tunggal seperti itu? Bukankah itu mengalahkan tujuan penggunaan paket?

Secara pribadi, saya perlu membuat beberapa INSERT ... ON DUPLICATE KEY UPDATE sekaligus. Saya tidak dapat melakukan penyisipan dengan banyak nilai karena saya ingin tahu baris mana yang telah diperbarui dan baris mana yang telah dibuat (memeriksa nilai baris yang terpengaruh).

Saya telah bermain dengan berbagai pembuat kueri untuk node.js setelah kami menemukan batasan Knex (yang pada dasarnya sering kali menggunakan kueri mentah). Pada akhirnya saya hanya membuat kode sql builder sendiri - QSql Demo . Ini tidak lengkap atau siap produksi, tetapi pada dasarnya merangkum cara saya berpikir.

Demo yang sangat keren, saya berencana menambahkan sesuatu seperti itu setelah rilis berikutnya - penasaran, apakah Anda sudah mencoba cabang 0.6?

Saya ingin tahu apakah ada ruang untuk kolaborasi di sini, di mana QSQL mungkin dapat memenuhi kebutuhan akan pembuat kueri yang lebih kuat, sementara Knex menangani penyatuan koneksi & menghaluskan kutil antara dialek yang berbeda.

Pokoknya, kerja bagus, saya akan memeriksanya!

Yah tidak, kami tidak mencoba 0.6 karena kami memiliki banyak kode menggunakan knex di satu tempat dan kemudian melakukan beberapa kueri mentah di tempat lain. Setelah beberapa penelitian saya memutuskan untuk mengganti seluruh tumpukan dengan sesuatu yang dapat dipertahankan dan di mana fitur dapat ditambahkan secara instan.

Yah, saya pikir pasti ada ruang untuk kolaborasi, namun akan memakan waktu untuk menstabilkan Qsql terlebih dahulu. Maksud saya mendefinisikan API yang bagus yang cocok dengan 99% kebutuhan dan kasus penggunaan (seperti yang Anda lihat beberapa konstruksi masih agak jelek) dan untuk menerapkan abstraksi yang tepat sehingga backend lain dapat ditambahkan.

mungkin kemampuan untuk melewatkan array kueri dan kemudian hasilnya adalah array juga? sesuatu yang sederhana seperti ini akan keren, bisa menggunakan sesuatu seperti async.parallel untuk melakukannya

@niftylettuce Bluebird sudah hadir dengan fitur "async.parallel" di luar kotak. Tapi ini bukan masalahnya. Kami benar-benar membutuhkan cara untuk mengeksekusi kueri yang dipisahkan koma dalam satu kali proses.

Bukankah ini mungkin dengan hanya menandai

    multipleStatements: true

di blok koneksi konfigurasi seseorang:
misalnya

    connection: {
        host: 'localhost',
        user: 'superSecret',
        password: 'notGonnaTell',
         port: 8889,
        database: 'babelverse_core',
        multipleStatements: true
    }

Oke jadi jika ada orang di sini yang tertarik - saya sedang mengerjakan beberapa hal baru di refactor yang akan datang untuk memungkinkan ini.

Saya bertanya-tanya tentang apa api yang ideal untuk ini ... apakah kita ingin memiliki satu rantai?

knex
  .update('records_raw')
  .set({title: x}).where({id: 1})
  .end()
  .update('records_raw')
  .set({title: y}).where({id: 2})
  .spread((resultA, resultB) => {

  })

atau sesuatu yang lebih seperti:

knex.multiQuery([
  knex.update('records_raw').set({title: x}).where({id: 1})
  knex.update('records_raw').set({title: y}).where({id: 2})
]).spread((resultA, resultB) => {

})

Juga melihat kemungkinan membuat kasus knex.raw bekerja secara otomatis dengan memisahkan titik koma.

Memisahkan titik koma pada kueri mentah akan menjadi awal yang baik.

Saya mengalami masalah sekarang di mana penghapusan saya tidak selesai tepat waktu sebelum penyisipan saya, jadi saya mendapatkan kesalahan kunci duplikat yang dilemparkan.

Saya melakukan sesuatu seperti

knex("mytable")
.where({
  id: 32423
})
.del()
.then( ->
  knex("mytable")
  .insert()
 ..... 

kamu paham intinya..

Del tidak selesai tepat waktu.

@tgriesser suara saya untuk knex.multiQuery

@tgriesser ada pembaruan pada kemampuan untuk mengeksekusi banyak pernyataan dalam satu kueri? Saya akan lebih memilih:

.update('records_raw')
.set({judul: x}).di mana({id: 1})
.akhir()
.update('records_raw')
.set({title: y}).where({id: 2})
.kemudian(fungsi(hasil){
hasil[0] //hasil 1
hasil[1] // hasil 2
})
.catch(fungsi(err) {
console.log(err);
});

+1

Untuk melakukan pemisahan yang tepat pada titik koma, kita perlu mengurai seluruh kueri mentah menggunakan parser khusus dialek. Khususnya, jika string berisi ; , mereka harus diabaikan. Berikut adalah dua contoh SQL valid yang tidak mudah diuraikan.

-- generic SQL
SELECT * FROM book WHERE title = 'Lord of the Rings; The Fellowship of the Ring';
-- MySQL specific
CREATE PROCEDURE dorepeat(p1 INT)
  BEGIN
    SET <strong i="9">@x</strong> = 0;
    REPEAT SET <strong i="10">@x</strong> = <strong i="11">@x</strong> + 1; UNTIL <strong i="12">@x</strong> > p1 END REPEAT;
  END

Perhatikan juga dalam kasus pertama kita akan berakhir dengan 2 pernyataan, jadi kodenya perlu memperhitungkan hasil kosong dari pemisahan.

Sejujurnya, secara keseluruhan, saya pikir ini adalah ide yang buruk. Ada terlalu banyak peluang untuk injeksi SQL dan tidak membagi kueri dengan benar.

Juga, saat menjalankan beberapa pernyataan, Anda hampir selalu menginginkan hal itu terjadi dalam transaksi yang sama. Knex telah memecahkan masalah ini dengan sangat baik dengan sintaks .transacting(function(transaction) { /* code */ }) . Dalam kasus yang jarang terjadi di mana Anda tidak memerlukan transaksi, Anda cukup menggunakan bluebird untuk bergabung dengan pernyataan knex Anda dan mendapatkan hasilnya.

Jadi karena masalah ini, menurut saya ini tidak boleh terjadi.

:-1:

Juga, saat menjalankan beberapa pernyataan, Anda hampir selalu menginginkan hal itu terjadi dalam transaksi yang sama. Knex telah memecahkan masalah ini dengan sangat baik dengan sintaks .transacting(function(transaction) { /* code */ }) . Dalam kasus yang jarang terjadi di mana Anda tidak memerlukan transaksi, Anda cukup menggunakan bluebird untuk bergabung dengan pernyataan knex Anda dan mendapatkan hasilnya.

Saya dapat memberi tahu Anda dari pengalaman bahwa dalam kasus tertentu, ada perbedaan kinerja _besar_ antara kedua pendekatan ini dan mengelompokkan beberapa nilai ke server sebagai satu perintah.

Saya mengerti ini sangat sulit untuk diimplementasikan dengan bersih, itulah sebabnya itu belum dilakukan, tetapi masalah ini sangat membatasi penggunaan knex saya untuk sebuah proyek.

Saya memiliki masalah serupa dan dapat menyelesaikannya. Lihat di sini. https://github.com/tgriesser/knex/issues/1075

Saya gagal melihat usecase apa pun untuk fitur ini. Akan senang untuk menutup tiket ini karena tidak akan diperbaiki. Jika seseorang bergantung pada urutan kueri yang dijalankan pada koneksi yang sama maka Anda mungkin ingin menggunakan transaksi.

Satu hal yang saya lihat yang membuat penggunaan 'beberapa kueri' ini menjadi sesuatu yang ingin kami miliki adalah bahwa ini merupakan potensi kemenangan kinerja dalam beberapa kasus. Dibutuhkan lebih sedikit perjalanan pulang pergi untuk mengomunikasikan semua kueri dan hasil ke dan dari server.

Tapi itu tidak seperti saya memiliki kasus penggunaan yang dapat diukur. Hanya berbicara dari pengalaman/ingatan masa lalu...

@jurko-gospodnetic Ini bisa terjadi ketika Anda tidak memiliki penyatuan koneksi. Dengan penyatuan, mengirim beberapa kueri pada dasarnya hanya memasukkan data ke soket TCP yang sudah dibuat. Juga jika kinerja Anda tergantung pada buffer TCP yang tidak cukup diisi maka knex sudah terlalu lambat untuk Anda :) Dalam hal ini lebih baik menggunakan driver secara langsung.

@jurko-gospodnetic berkomentar pada 20 Mei

Satu hal yang saya lihat yang membuat penggunaan 'beberapa kueri' ini menjadi sesuatu yang ingin kami miliki adalah bahwa ini merupakan potensi kemenangan kinerja dalam beberapa kasus. Dibutuhkan lebih sedikit perjalanan pulang pergi untuk mengomunikasikan semua kueri dan hasil ke dan dari server.

Saya menggunakan node-mysql (mysqljs) pada proyek kelas perusahaan untuk alasan khusus ini. Kami ingin memigrasikan seluruh proyek ke knex.js karena banyak alasan

Ada peningkatan kinerja yang cukup besar pada operasi semacam itu yang saya manfaatkan dengan memanfaatkan fitur kueri beberapa pernyataan dari driver node-mysql (mysqljs) .

Ini mungkin terbukti menjadi penghalang bagi saya jika Knex tidak mendukung ini. Jadi saya akan menghidupkan kembali utas ini untuk menanyakan hal itu.

Apakah banyak kueri dalam satu pernyataan didukung di Knex.js sekarang?

@nicholaswmin dapatkah Anda memberi tahu sedikit lebih spesifik apa yang Anda maksud dengan "peningkatan kinerja yang cukup besar"? Bagaimana mengirim banyak kueri dengan cara ini jauh lebih efisien daripada hanya mengirim beberapa kueri melalui koneksi yang sama? Ada benchmark?

@elhigu Tidak ada tolok ukur tetapi langsung saya dapat mengatakan ini:

Satu panggilan ke DB (dikemas dengan semua pernyataan) menghilangkan bolak-balik jaringan server-db.

Di sisi lain, mengirim beberapa kueri melalui koneksi yang sama bukanlah hal yang mudah. Solusi ini hanya akan bekerja dengan aliran non-transaksional, lihat Masalah ini

@nicholaswmin Apakah Anda mengirim sejumlah besar kueri kecil dan sebagian besar diabaikan atau hasil kecil? Dalam hal ini saya kira perbedaannya mungkin terlihat, karena memungkinkan driver untuk mengemas beberapa pertanyaan ke setiap paket TCP, di mana jika tidak, setiap paket TCP akan memiliki sebagian besar header dan jumlah muatan yang sangat kecil.

Perbedaan kinerja semacam itu harus dapat diukur dengan mudah bahkan dari jumlah lalu lintas jaringan.

Saya tidak tahu apakah driver mendukung pengiriman banyak kueri dari panggilan connection.query ke DB tanpa terlebih dahulu menunggu hasil panggilan sebelumnya. Jika ya, seharusnya tidak ada banyak perbedaan dalam lalu lintas TCP antara mengirim beberapa connection.query atau multi-kueri tunggal yang didukung oleh driver mysql.

@elhigu Mereka memang kueri kecil tetapi hasilnya diperlukan agar dapat digunakan lebih jauh di rantai transaksi/kueri.

Apakah ada cara bagi saya untuk melihat paket TCP yang dikirim melalui debug atau opsi serupa? Bukan kueri itu sendiri yang dikirim tetapi paket yang sebenarnya.

Sebuah kasus penggunaan:

Saat memperbarui data Pengguna di sistem saya, saya ingin menghitung Jejak Audit (apa yang telah berubah) untuk Pengguna tersebut.

// # PSEUDOCODE

// get current data of user
getUserData();
// set data of user
setUserData()
// get new data of user
getUserData()
// compute the audit trail by comparing the difference between before-set/after-set datums
computeAuditTrail(previousData, newData);

Setiap panggilan di atas melakukan beberapa panggilan DB, jadi seperti yang dapat Anda bayangkan, ini adalah banyak perjalanan pulang pergi jaringan.

Seperti yang dinyatakan pada #1806 saya dapat mengatasi ini dengan menggunakan Promise.all() untuk kueri yang tidak perlu berurutan (kebanyakan panggilan DB di getData/setData tidak perlu berurutan).

Itu akan bekerja dengan aliran non-transaksional, karena kueri dapat dikirim pada koneksi yang berbeda dari kumpulan. Segera setelah saya menggunakan transaksi untuk melakukan alur di atas, itu melambat (sekitar 4x) karena kueri dikirim pada satu koneksi.

Sebagai catatan tambahan saya menggunakan MSSQL.

Wireshark adalah alat lintas platform yang cukup umum digunakan untuk menganalisis lalu lintas jaringan.

Saya tidak percaya bahwa ada cara untuk melihat level itu dengan node.js.

Salah satu cara mungkin menggunakan iptraf atau apa pun untuk mengukur jumlah data yang dikirim/diterima saat mengirim jumlah kueri yang sama dengan dikemas dalam satu kueri atau diteruskan secara terpisah ke driver.

Saya ingin melihat sintaks multiQuery .

Perubahan apapun ?
Apakah mungkin sekarang membuat multiQuery?
Jika demikian, menggunakan sintaks apa?

var knex = require("knex");
var _ = require("lodash");
var Promise = require("bluebird");

var knex = require('knex')({
    client: 'sqlite3',
    connection: {
        filename: "./data.sqlite"
    }
});

// Create Schema
let createScript = `

CREATE TABLE Class ( 
    Id                   integer NOT NULL  ,
    Name                 varchar(100)   ,
    CONSTRAINT Pk_Classes_Id PRIMARY KEY ( Id )
 );

CREATE TABLE Material ( 
    Id                   integer NOT NULL  ,
    Description          varchar(500)   ,
    CONSTRAINT Pk_Material_Id PRIMARY KEY ( Id )
 )

-- ... and so on (leave off the last semi or remove it later) ... 

`;

let statementPromises = _.map(createScript.split(';'), (statement) => {
    return knex.raw(statement);
});

Promise.all(statementPromises).then(function() {
    console.log('Schema generated. Populating...');

     // ...

itu hanya satu lingkaran, bukan beberapa permintaan yang dibuat menjadi satu, dan itu LAMBAT

@mscheffer pertanyaannya adalah:

Saya menghadapi masalah bahwa saya ingin menjalankan beberapa kueri yang dipisahkan oleh ';' oleh satu eksekutif, apakah itu mungkin?

Yang jawaban saya memecahkan. Jika Anda memiliki solusi yang lebih cepat dan lebih baik, berikan.

@VictorioBerra Memisahkan dari ; tidak berfungsi dalam kasus umum, karena mungkin ada ; di dalam literal string dan komentar. Jika Anda membaca di data dump, saya akan mengatakan bahwa cara termudah adalah dengan menyalurkan kode dump SQL ke shell sqlite. Sayangnya itu tidak berfungsi untuk database dalam memori, karena mereka hanya koneksi tunggal.

Juga dalam kasus Anda, di mana Anda membuat string SQL di dalam string template dan kemudian memisahkannya, menggunakan knex.schema.* builder secara efektif hal yang sama, tetapi lebih aman.

Sejauh yang saya tahu tidak ada cara untuk mencapai fitur menjalankan banyak kueri ini dalam satu perintah untuk semua driver dialek. Saya pikir itu adalah mysql dan Oracledb atau mssql, yang memiliki dukungan itu dan dengan mysql Anda masih mendapatkan hasil dari kueri terakhir sebagai respons (yang mungkin baik-baik saja).

Ingatlah untuk menggunakan transaksi jika Anda memodifikasi data menggunakan beberapa kueri sehingga Anda dapat memastikannya berjalan berurutan!

EKSEKUSI GANDA QUERY MENGGUNAKAN RAW SQL
Saya mengalami situasi serupa di mana salah satu migrasi knex kami perlu menjalankan CREATE DATABASE beberapa kali berturut-turut. Saya berharap untuk memisahkan kueri dengan titik koma, tetapi knex membuat kesalahan sintaks. Solusinya adalah memasukkan "multipleStatements": true di objek koneksi Anda, sehingga hasil akhirnya mungkin terlihat seperti ini:

"host": "192.168.x.x",
"user": "userLogin",
"password": "userPassword",
"database": "schemaToUse",
"multipleStatements": true

Setelah itu, Anda dapat menggunakan pernyataan mentah seperti:

return knex.raw("CREATE DATABASE schema0; CREATE DATABASE schema1; CREATE DATABASE schema2")
   .then((result) => {
   })
   .catch((error) => {
   });

EKSEKUSI GANDA QUERY MENGGUNAKAN KNEX QUERY BUILDER
Jika Anda perlu menggunakan pembuat kueri knex alih-alih menulis SQL mentah sendiri, maka Anda harus mengonversi hasilnya dalam Knex.QueryBuilder menjadi string, lalu menggabungkan beberapa kueri. Berikut salah satu contohnya:

// Suppose that we wanted to add 100 currency for all players in multiple games
const addCurrency = 100;
const updateCurrency = { currency: "currency + " + addCurrency };
const queries = [
   knex.table("game0.stats").update(updateCurrency),
   knex.table("game1.stats").update(updateCurrency),
   knex.table("game2.stats").update(updateCurrency),
];
const multiQuery = queries.join(";");
console.log(multiQuery);
return knex.raw(multiQuery)
   .then((result) => {
   })
   .catch((error) => {
   });

Ini juga membantu mengurangi kesalahan manusia saat menulis kueri yang berlebihan yang perlu dijalankan secara berurutan. Sekali lagi dengan sesuatu seperti ini saya akan menyarankan untuk membungkusnya dalam sebuah transaksi.

Saya pikir ini dapat ditutup karena ini sebagian besar dibatasi oleh driver db. Setidaknya permintaan fitur yang tepat diperlukan.

@AksharaKarikalan bahkan jika Anda melakukan banyak subkueri dan pengurangan di antaranya, Anda masih membuat hanya satu kueri. Jadi saya menghapus komentar yang tidak relevan dengan masalah ini. Stackoverflow adalah tempat yang tepat untuk permintaan penggunaan knex.

Apakah halaman ini membantu?
0 / 5 - 0 peringkat

Masalah terkait

nklhrstv picture nklhrstv  ·  3Komentar

npow picture npow  ·  3Komentar

zettam picture zettam  ·  3Komentar

hyperh picture hyperh  ·  3Komentar

fsebbah picture fsebbah  ·  3Komentar