Bulk insert with mysql2 and NodeJs throws 500 - mysql

I have a method which I want to bulk insert into mysql. I am using NodeJS and mysql2.
My method:
createWorklog = async ({ sqlArray }) => {
const sql = `INSERT INTO ${this.tableName}
(project_id, user_id, date, duration, task, description) VALUES ?`
const result = await query(sql, [sqlArray])
const affectedRows = result ? result.affectedRows : 0;
return affectedRows;
}
Where sqlArray is an array of arrays where all the children arrays are the same length.
And the query method that is called in this method is the next one:
query = async (sql, values) => {
return new Promise((resolve, reject) => {
const callback = (error, result) => {
if (error) {
reject(error);
return;
}
resolve(result);
}
// execute will internally call prepare and query
this.db.execute(sql, values, callback);
}).catch(err => {
const mysqlErrorList = Object.keys(HttpStatusCodes);
// convert mysql errors which in the mysqlErrorList list to http status code
err.status = mysqlErrorList.includes(err.code) ? HttpStatusCodes[err.code] : err.status;
throw err;
});
}
}
My problem is that the body parameters are ok (as I said, array of arrays) but the method throws 500.
Can this be possible because of execute command that is present in mysql2? Or is another mistake?
Thank you for your time!
EDIT
I changed my method from using 'execute' to 'query' Based on #Gaurav’s answer and it's working well.

This is a known issue with execute and query method in mysql2
I've found a working alternative.
createWorklog = async ({ sqlArray }) => {
const sql = `INSERT INTO ${this.tableName}
(project_id, user_id, date, duration, task, description) VALUES ?`
const result = await query(sql, [sqlArray], true) // adding true for multiple insert
const affectedRows = result ? result.affectedRows : 0;
return affectedRows;
}
Then query can be written as below
return new Promise((resolve, reject) => {
const callback = ...
if (multiple) this.db.query(sql, values, callback);
else this.db.execute(sql, values, callback);
}).catch(err => {
...
...
});
}
}
More info regarding this issue can be found here https://github.com/sidorares/node-mysql2/issues/830

Related

How to implement a map nested MySQL query in Node that populates a property of each element

I've searched this so many ways and can't seem to get a solution that works for me - perhaps because I'm new to JS and the callback "hell" as I've seen it described.
I first run a query that returns a list of results ("FailTypes"), then I want to iterate over each item in the list and add a property ("FailTypeAreaScores") which itself requires another query. I figured the map function would be the best to do this.
Here's the calling function, 1st query:
static async getFailTypes(eq, callback) {
const sql = 'CALL getFailTypes(?)';
db.query(sql, eq, async (err, rows) => {
let result = Object.values(JSON.parse(JSON.stringify(rows)));
let fts = await Promise.all(result[0].map(async ft => {
const newFt = await getFailTypeAreaScores(ft, (err, data) => {return data})
return newFt }));
if (err){
console.log(err.message);
} else {
callback(null, fts);
};
});
};
Here's the nested query:
async function getFailTypeAreaScores(ft, callback){
const sql = 'CALL getFailTypeAreaScores(?);';
db.query(sql, ft.ID, async(err, rows) =>{
let result = Object.values(JSON.parse(JSON.stringify(rows)));
if (err){
console.log(err.message);
} else {
ft.ftas = result[0];
callback(null, ft);
}
});
};
I've tried various methods of promises and callbacks and nothing seems to work. Right now, I'm getting the data back as expected at the {return data} and if I replace that with a {console.log(data)} I can see the new ft with the added property in the console.
However, it doesn't seem to get out of that function and back up to the map to replace the original ft.
So, my resulting callback(null, fts) just returns a list of null.

Why can't I use a local variable after a SQL query in Node.js?

I have to select all tags available in the database, so I could use them in my function, but I can't use the variable after I assigned a value to the variable in the callback function of the query, why?
Code which is not working:
let tags = [];
db.query(
`select * from tags`,
(err, results) => {
tags = results;
}
)
console.log(tags);
return;
However this works:
let tags = [];
db.query(
`select * from tags`,
(err, results) => {
tags = results;
console.log(tags);
}
)
return;
but why? I want to use the variable tags after that query again, but somehow the value assigned to it is destroyed after the query. What would I have to change?
Javascript is asynchronous language, db.query is a network call which will be asynchronous, so if you want to use the response of the query it has to be called after the db.query is executed.
In the first case console.log(tags); runs before db.query is executed
and you are getting undefined response.
In the second case console.log(tags); runs after db.query is executed thats why you are getting the response from the query.
You use this using Promise:
async function queryExec() {
let tags = await promiseQuery(`select * from tags`);
console.log(tags);
return tags;
}
function promiseQuery(query) {
return new Promise((resolve, reject) => {
db.query(query, (err, results) => {
if (err) {
return reject(err);
}
resolve(results);
})
})
}
With async/await:
async function queryExec() {
let tags = await db.query(`select * from tags`);
console.log(tags);
return tags;
}
Query is an async function, so the value hasnt returned by the time you try to print it.
within the callback function however, you already have the return value you expect.
If you need it to run synchronously you could await the query function, and have the value in hand.
If you set your function be async then you will be able to await the result.
something like:
const foo = async () => {
let tags = [];
tags = await db.query(
`select * from tags`
);
console.log(tags);
return;
}

NodeJs Synchronous MYSQL Query and Foreach

I have some mysql Query's in NodeJs that depends to a above result. Here is my code:
const sms = async function() {
await connection.query('select * from sms where sms.status_id = 2',
(err, rows) => {
if (err) throw err
rows.forEach(row => {
startdate = moment(row.statdate).format('YYYY-MM-DD HH:mm:ss');
enddate = moment(row.enddate).format('YYYY-MM-DD HH:mm:ss');
connection.query('select * from positions where sms_id = "'+row.id+'" and verified is null order by date asc',
(err, rows2) => {
if (err) throw err
rows2.forEach(row2 => {
connection.query('HERE IS A QUERY TO INSERT A REGISTER IN ANOTHER TABLE', data, (err,res) => {
if (err) throw err
console.log(`Inserted ${res.insertId}`);
})
})
}
);
})
}
)
The problem is, that way, I don't have the expected result in the middle query.
How can i do this synchronous?
I try to use Promises, but i don't understand how to do.
In most cases, you should avoid synchronous functions, especially if running server operations.
Rather, I'd recommend using mysql2 and it's promise wrapper.
Then you can do:
const mysql = require('mysql2/promise');
...
// use promise, then, catch
const sms = async function() {
return connection.query('select * from sms where sms.status_id = 2')
.then(([smss]) =>
smss.map(row => {
connection.query('select * from positions where sms_id = "'+row.id+'" and verified is null order by date asc')
.then(([positions]) => positions)) // unwrap array
)
.then(positions =>
positions.map(row => connection.query('...'))
)
.catch(e => {
// process errors
});
};
// or use awaits
const sms = async () => {
let [smss] = await connection.query('select * from sms where sms.status_id = 2');
let positions = await Promise.all(smss.map(row => {
...
});
};
Alternatively, you can use some better SQL queries to simplify the process. Like this:
select *
from sms
join positions on (positions.sms_id = sms.id)
where sms.status_id = 2
and positions.verified is null
order by positions.date

async function with mysql query won't return query results node.js

I have a function that queries a mysql database with a select query. This is all inside an async function. The query keeps telling me on the return line that it cannot find the variable "rollStatus".
async function PullRollStatus(){
return new Promise((resolve,reject)=>{
var sql = `SELECT * FROM brawlid${brawlID}`
con.query(sql, (error, rows, fields) => {
var rollStatus= []
for (var i in rows) {
rollStatus.push(rows[i].Bakuganrolled)
}
})
console.log(rollStatus)
return rollStatus
})
}
var rolledcheck = await PullRollStatus();
console.log(rolledcheck)
I've never used new Promise before, as async functions are still kind of new to me. I have tried this without the "return new Promise" line and with it, both giving the same result. I have referenced this
async and await on MySQL call in node js and I'm still getting some problems or it might be confusing me more, I don't know. Any help is greatly appreciated. Thanks in advance!
I would have to know a bit more about the database and the value of brawlID, but you don't use return with promises instead you use resolve and reject, also, you are returning a promise, do you don't use async. Here is an edited example. Note I use mysql.format to pass the variable into the query, this will safeguard against code injection.
Also, I would think you would be using a where statement, unless you have a table for each brawlID, but it would make for sense if brawlID is a column in the table. I changed the function to take the value of brawID passed parameter instead of referencing a global variable.
const mysql = require("mysql2/promise");
const mysqlconfig = {
host: "localhost",
user: "youruser",
password: "yourpassword"
database: "yourdb"
multipleStatements: true
};
const con = mysql.createConnection(msqlconfig);
function to (promise) {
return promise
.then(val => [null, val])
.catch(err => [err]);
}
function PullRollStatus(brawlID){
return new Promise((resolve,reject)=>{
let sql = `SELECT * FROM brawlid WHERE brawlID=?`;
mysql.format(sql,brawlID);
con.query(sql, (error, rows, fields) => {
if (error) {
reject(error);
} else {
let rollStatus = [];
for (let row of rows) {
rollStatus.push(row.Bakuganrolled)
}
console.log(rollStatus);
resolve(rollStatus);
}
});
});
}
let brawlIDtoCheck = 1;
let [err,rolledcheck] = await to(PullRollStatus(brawlIDtoCheck));
if (err) {
console.log("encountered err",err);
}
console.log(rolledcheck)

Is there a way to pass a value from a mysql callback function to the outer function in express?

I'm using express and npm MySQL to develop an API.I have a json request in this format:
{
"payments":[
{
"PolicyNo": "ME3",
"PaymentDate": "2019-04-16T18:00:00.000Z",
},
{
"PolicyNo": "PIN001q",
"PaymentDate": "2019-04-16T18:00:00.000Z",
}]
}
I want to check the database if the policyNo exists before inserting. To avoid the common ERR_HTTP_HEADERS_SENT, I've looped through the payments querying the database with the PolicyNo. If it exists it's pushed into a success array if it doesn't it's pushed into a failed array.
This works perfectly but I can't access these arrays outside the callback.
Here's what I've tried:
router.post('/bla', (req, res)=>{
const values = []
const failedvalues = []
let sql = 'SELECT PolicyNo from pinclientinfo WHERE PolicyNo=?'
req.body.payments.forEach(element => {
connection.query(sql,element.PolicyNo,(err, rows) =>{
if(!err){
if(rows && rows.length > 0){
values.push(element.PolicyNo, element.PaymentDate)
}else{
failedvalues.push(element.PolicyNo)
}
}
})
})
res.json({
failed:failedvalues,
success:values
})
})
Here's the response I'm getting:
{
"failed": [],
"success": []
}
This has some major problems, mostly conceptually.
Firstly the forEach is synchronous will be called payments.length number of times, but the sql query is Asynchronous so it will complete in the future.
I think you are confused between synchronous and asynchronous functions and how they work.
But you can solve this (in your case) atleast two ways.
1) Use the IN syntax and get the array. Iterate over it and do stuff. "SELECT PolicyNo from pinclientinfo WHERE PolicyNo in (...)"
let sql = 'SELECT PolicyNo from pinclientinfo WHERE PolicyNo IN (' + Array(req.body.payments).fill('?').join(',') + ')'
const policies = req.body.payments.map(p => p.PolicyNo);
const values = [];
const failedvalues = [];
connection.query(sql, ...policies, (err, rows) => {
//now check each row..
rows.forEach(element => {
//Not optimized only depicts logic
///do stuff
/// like fill values and failedvalues
if(policies.indexOf(element.PolicyNo) > -1){
values.push(...)
}else{
failedvalues.push(...)
}
});
res.json({
failed: failedvalues,
success: values
})
})
Which will be 1 DB call.
2) The other approach is (not very good) doing multiple db calls and check for count.
let sql = 'SELECT PolicyNo from pinclientinfo WHERE PolicyNo=?'
let count = 0;
req.body.payments.forEach(element => {
connection.query(sql, element.PolicyNo, (err, rows) => {
if (!err) {
if (rows && rows.length > 0) {
values.push(element.PolicyNo, element.PaymentDate)
} else {
failedvalues.push(element.PolicyNo)
}
}
// check If all Complete
count+=1;
if(count === req.body.payments){
//all complete
res.json({
failed: failedvalues,
success: values
})
}
})
})
BUT SERIOUSLY, USE PROMISE. USE ASYNC/AWAIT USE THOSE SWEET LITTLE FEATURES ES6 GIVES YOU
Check out: this post
because connection.query is asynchronous, so return:
{
"failed": [],
"success": []
}
use promise and await you can synchronized resolve mysql data
use Promise.all() you can synchronized resolve list of promise
router.post("/bla", async (req, res) => {
let values = [];
let failedvalues;
let promises = [];
let sql = "SELECT PolicyNo from pinclientinfo WHERE PolicyNo=?";
req.body.payments.forEach(element => {
promises.push(
new Promise(function(resolve, reject) {
connection.query(sql, element.PolicyNo, (err, rows) => {
if (!err) {
if (rows && rows.length > 0) {
values.push(element.PolicyNo, element.PaymentDate);
} else {
failedvalues.push(element.PolicyNo);
}
}
resolve();
});
})
);
});
await Promise.all(promises);
res.json({
failed: failedvalues,
success: values
});
});