Failed to Update MySQL table using Express JS - mysql

I am facing difficulties to update the following tables (MySQL) by using Express Js.
productdb.discount
discount_id | discount_name | discount_percent
productdb.product
product_id | discount_id | product_price(original price) | product_latest_price(price after discount) |
It works on this way :
User update the existing discount table by inserting new discount name, percent and select relevant product that applies the respective discount from the checkbox. The input form is as below:
Hence when submit, it will return the discount info, including the discount_id, and the selected product in array-form according to the assigned product_id.
My concept of updating the database table is :
First, Update the discount table.
Then, Update all discount_id in product table to null according to the discount_id.
Update the product latest price to product price.
Update the discount_id to the product table according to the submitted product array.
Finally, Update the product_latest_price by calculating the result after multiple with discount_percent.
I wrote the Query in Express Promise based and it doesn't execute correctly as it failed to update the product_latest_price(Possibly due to async method). In fact, the code looks messy. Anyone have any idea on updating these tables in a more efficient way, or much cleaner code?
By the way, my code to execute the query is as below.
exports.discount_update = (discount_info, discount_id, product_id) => {
return new Promise((resolve, reject) => {
connectionPool.getConnection((connectionError, connection) => {
if (connectionError) {
reject(connectionError);
} else {
connection.query(`UPDATE productdb.discount SET ?
WHERE discount_id = ?`, [discount_info, discount_id], (error, result) => {
if (error) {
reject(error);
} else {
connection.query(`UPDATE productdb.product
SET discount_id = null, product_latest_price = product_price
WHERE discount_id = ?`, [discount_id], (error, result) => {
if (error) {
reject(error);
} else if (result.affectedRows >= 1) {
for (i = 0; i < product_id.length; i++) {
connection.query(`UPDATE productdb.product
SET discount_id = ?
WHERE product_id = ?`, [discount_id, product_id[i]], (error, results) => {
if (error) {
reject(error);
} else {
connection.query(`UPDATE productdb.product AS product
JOIN productdb.discount AS discount
ON product.discount_id = discount.discount_id
SET product.product_latest_price = product.product_price - product.product_price * (discount.discount_percent) / 100
WHERE product_id = ?`, [product_id[i]], (error, result) => {
if (error) {
reject(error);
} else if (result.affectedRows >= 1) {
resolve(result)
} else {
resolve(result)
}
})
}
})
}
}
})
}
})
}
})
})
}

Related

Increment a value on MYSQL with react node.js

Why does this doesn't work
const increment = 'votes + 1'
db.query("UPDATE president SET votes = ? WHERE nickname = ?",
[increment, president], (err, result) => {
if (err) {
console.log(err)
} else {
console.log(result)
}
})
but this code below works
db.query("UPDATE president SET votes = votes + 1 WHERE nickname = ?",
[president], (err, result) => {
if (err) {
console.log(err)
} else {
console.log(result)
}
})
I just want to do the incrementing of mysql columns with votes = ?
I wanted to do it with the votes = ? way. Does anyone know the proper syntax of incrementing a value in mysql with react node js?

Query is not executed inside of a loop function nodejs MySQL

I am a beginner in NodeJs and I'm trying to run a SQL query inside of a while, but it is not executing the query. I have a function in which I have an array of data from the database, if I got results, I'll save them in 2 arrays, then I declared an auxiliar variable for the while loop. Inside of the while I have to select all the members that have the reference_id in one of the arrays. If I get data, I have to save the data in the 2 arrays again and repeat the operation until there is no data from the database. The problem is that the query and all the operations that are inside of the query are not working and I don't know why.
everything inside of the loop is being executed just the query is the one that is not being executed.
const membersPiramid2 = (request, response) => {
let member_id = request.params.member_id;
let members_ids = [];
let members = [];
let aux = true;
db.query(`SELECT member_id FROM structures WHERE reference_id = '${member_id}'`, (error, results) => {
if (error) throw error;
if (results.length > 0) {
members_ids = results.map(r => r.member_id);
members.push(results);
while (aux) {
db.query(`SELECT member_id FROM structures WHERE reference_id IN ('${members_ids}')`, (err, newMembers) => {
if (err) throw err;
if (newMembers.length > 0) {
members.push(newMembers);
members_ids.length = 0
members_ids = newMembers.map(m => m.member_id)
} else {
aux = false
}
})
}
}
response.send(members);
I tried using promises and async functions but if I use any loop it is still not working
function submembersPiramid(members_ids) {
return new Promise((resolve, reject) => {
db.query(`SELECT member_id FROM structures WHERE reference_id IN ('${members_ids}')`, (err, newMembers) => {
console.log('hola2')
if (newMembers.length > 0) {
resolve(newMembers)
} else {
reject('No data found')
}
})
})
}
const membersPiramid = (request, response) => {
let member_id = request.params.member_id;
let members_ids = [];
let members = [];
let aux = true;
db.query(`SELECT member_id FROM structures WHERE reference_id = '${member_id}'`, (error, results) => {
if (error) throw error;
if (results.length > 0) {
members_ids = results.map(r => r.member_id);
members.push(results);
async function doFunction() {
await submembersPiramid(members_ids).then(response => {
if (response != 'No data found') {
members.push(response);
members_ids.length = 0;
members_ids = response.map(r => r.member_id)
} else {
aux = false;
}
}).then()
}
while(aux){
doFunction()
}
}
response.send(members);
})
}
Any idea why the query does not work in any loop?
Here is the query result
enter image description here
Something like this is likely to be simpler and many times more efficient:
SELECT p.member_id as parent_member_id, c.member_id as child_member_id
FROM structures p
INNER JOIN structures c on c.reference_id = p.member_id
WHERE p.reference_id = '${member_id}'
While we're here, this looks like it would be crazy-vulnerable to sql injection. Sql injection is big deal, so take a few moments to make sure you understand what sql injection is and your platform's mechanism to use prepared statements/parameterized queries.
instead of two queries , you can do it all in one alone
db.query(`SELECT member_id FROM structures WHERE reference_id IN (SELECT member_id FROM structures WHERE reference_id = '${member_id}') UNION SELECT member_id FROM structures WHERE reference_id = '${member_id}'`
, (err, newMembers) => {
if (err) throw err;
if (newMembers.length > 0) {
members.push(newMembers);
members_ids.length = 0
members_ids = newMembers.map(m => m.member_id)
} else {
aux = false
}
})
in case you really want to use nested queries, which in rare occasions it is necessary, you should see the solutions here where the asyncron communicaion is mandatory Nested query in node js using mysql
and when we are at it, you colud always look also this about sql injection up Preventing SQL injection in Node.js
the query to get all member_ids must be
SELECT
member_id
FROM
structures
WHERE
reference_id IN (SELECT
member_id
FROM
structures
WHERE
reference_id = '${member_id}')
UNION SELECT
member_id
FROM
structures
WHERE
reference_id = '${member_id}'

Mysql, Node, query within a query, how to populate property in map function from another query

Firstly, if anyone can edit my question title or question to make more sense, please do.
I have a node/express app making mysql queries with mysql.js. I have a query that looks up a table of questions and then runs a map function on the results. Within that map function, I need to query another table, of answers, corresponding to each record in the questions table. The value I need is the number of answers to that question, ie the number of records in each answers table. I've tried all kinds of different examples, but nothing quite fits my case in a way that makes sense to me. New at Node and Express, and even MySQL so having a hard time picking out quite what to.
I understand that the problem is the async nature of node. getAnswersCount() returns "count" before the query finishes. Below is my code. Need some advice on how to achieve this.
The value 123 is assigned to count just to clarify the trace results.
app.get('/', (req, res) => {
db.query('SELECT * FROM questions LIMIT 0, 100',
(error, results) => {
if (error) throw error;
questions = results.map(q => ({
id: q.id,
title: q.title,
description: q.description,
answers: getAnswersCount( q.id )
}));
res.send( questions );
});
});
const getAnswersCount = ( id ) =>
{
const tableName = 'answers_' + id;
var count = 123;
var sql = `CREATE TABLE IF NOT EXISTS ${tableName}(
id int primary key not null,
answer varchar(250) not null
)`;
db.query( sql,
(error, results) => {
if (error) throw error;
//console.log( 'answers table created!' );
});
sql = `SELECT COUNT(*) AS answersCount FROM ${tableName}`;
db.query( sql,
(error, results) => {
if (error) throw error;
//console.log( count ); // will=123
count = results[0].answersCount;
//console.log( count ); // will = results[0].answerCount
});
// I know this code runs before the query finishes, so what to do?
//console.log( count ); //still 123 instead of results[0].answersCount
return count;
}
EDIT: After attempting various versions of Michael Platt's suggestion in his answer without success, I finally worked out a solution using Express callbacks and a promise, adding the answers values to the questions array afterwards:
app.get( '/', (req, res, next ) => {
db.query('SELECT * FROM questions LIMIT 0, 100',
(error, results) => {
if (error) throw error;
questions = results.map(q => ({
id: q.id,
title: q.title,
description: q.description,
}));
next();
});
}, (req, res ) => {
questions.map( currentElem => {
getAnswersCount( currentElem.id ).then( rowData => {
currentElem.answers = rowData[0].answersCount;
if( currentElem.id == questions.length ) res.send( questions );
});
});
});
const getAnswersCount = ( id ) => {
const tableName = 'answers_' + id;
var sql = `CREATE TABLE IF NOT EXISTS ${tableName}(
id int primary key not null,
answer varchar(250) not null
)`;
db.query( sql,
(error, results) => {
if (error) throw error;
//console.log( 'answers table created!' );
});
sql = `SELECT COUNT(*) AS answersCount FROM ${tableName}`;
return new Promise( ( resolve, reject ) => {
db.query( sql, ( error, results ) => {
if ( error ) return reject( err );
resolve( results );
});
});
}
I'm not sure which database module you are using to connect to and query the database but you could make the method async and then await the response from the query like so:
const getAnswersCount = async ( id ) =>
{
const tableName = 'answers_' + id;
var count = 123;
var sql = `CREATE TABLE IF NOT EXISTS ${tableName}(
id int primary key not null,
answer varchar(250) not null
)`;
var results = await db.query(sql);
sql = `SELECT COUNT(*) AS answersCount FROM ${tableName}`;
var count = db.query(sql)[0].answerCount;
// I know this code runs before the query finishes, so what to do?
//console.log( count ); //still 123 instead of results[0].answersCount
return count;
}
app.get('/', async (req, res) => {
db.query('SELECT * FROM questions LIMIT 0, 100',
(error, results) => {
if (error) throw error;
questions = results.map(q => {
const answerCount = await getAnswersCount( q.id )
return {
id: q.id,
title: q.title,
description: q.description,
answers: answerCount
}
}));
res.send( questions );
});
});
I think that will give you what you want and run correctly but it might require a bit of tweaking. You may need to async the function on the actual route itself as well and await the call for getAnswersCount but that should just about do it.

Bringing separate asynchronous branches back into one branch again

I am working on a node js app which makes use of the express and mysql libraries.
I have a MySQL user table with the following columns:
auto incrementing primary id
username varchar unique
There is no password, etc.
Other tables include:
room
id
room_name
user_room
id
user_id (FK to user table)
room_id (FK to room table)
details
id
user_room_id (FK to user_room table)
col1
col2
col3
Upon trying to connect to a room, I want the database to try pulling their data for that room.
If the data does not exist, I want to see if the username exists in the user table.
If the username does exist, I want to get their id.
If the username does not exist, I want to add their name to the user table and capture the last inserted id
Once having their id, I want to add a record to the user_room table for that user and then several records to the details table based on the newly inserted id in the user_room table.
I seem to be getting into a tangled web going into so many layers.
This is what my code currently looks like:
socket.on('enter room', function(data, callback){
var sql = "select col1, col2, col3 from room JOIN user_room on room.id = user_room.room_id JOIN user on user_room.user_id = user.id JOIN details on user_room.id = details.user_room_id where username = ?";
db_connection.query(sql, [socket.nickname], function (err, result) {
if (err){
console.log("ENTER ROOM DB ERROR: " + err);
return;
}
if (!result.length){
var sql = "select id from user where name = ?";
db_connection.query(sql, [socket.nickname], function (err, result){
if (err){
console.log("ENTER ROOM, SELECT ID DB ERROR: " + err);
return;
}
if (!result.length){
var sql = "insert into user (name) values (?)";
db_connection.query(sql, [socket.nickname], function(err, result){
if (err){
console.log("ENTER ROOM, INSERT ID DB ERROR: " + err);
return;
}
id = result.insertId;
});
}
else {
id = result[0].id;
}
});
//We need to pull things back into one branch again here
//Using the user id and room id I will insert a record into the user_room table
//Then using the newly inserted id in the user_room table, I need to add records to a details table
}
});
//Send col1, col2, and col3 data back to user
//This section here also needs to be pulled back into one branch again
io.sockets.emit('details', result);
});
It mostly works, but because I branch off in two different ways to get the user id (one if it already exists, and one if I need to insert it), I do not know how to pull it back together again into one branch.
What can I do to pull my code back into one branch again so that I can use the id again? Or, is there a better way of approaching this altogether?
A side question: Can I safely remove the "callback" in my opening function, or should I be using this somewhere in my code? I feel that the emit is like a callback to the client so that I do not need "callback" here.
I took a different approach to get userId on upsert. I used promise to send the room data immediately, if available.
socket.on('enter room', function (data, callback) {
let nickName = '';
let roomId = '';
return bookingDetails(nickName).then((details) => {
if (details.length !== 0) {
return Promise.resolve(details);
} else {
return createRoom(nickName, roomId);
}
}).then((details) => {
io.sockets.emit('details', details);
});
});
function createRoom(nickName, roomId) {
return getUserDetails(nickName).then((userId) => {
return insertUserRoom(userId, roomId); //your function
}).then((userRoomDetails) => {
return insertDetails(userRoomDetails); //your function
});
}
function bookingDetails(nickName) {
let sql = "select col1, col2, col3 from room " +
"JOIN user_room on room.id = user_room.room_id " +
"JOIN user on user_room.user_id = user.id " +
"JOIN details on user_room.id = details.user_room_id where username = ?";
return new Promise((resolve, reject) => {
db_connection.query(sql, [nickName], function (err, details) {
if (err) {
return reject("ENTER ROOM DB ERROR: ");
}
return resolve(details);
});
});
}
function getUserDetails(nickName) {
return new Promise((resolve, reject) => {
let sql = "select id from user where name = ?";
db_connection.query(sql, [nickName], function (err, userDetail) {
if (err) {
return reject(err);
}
if (userDetail === null) { //insert
return createUser(nickName);
}
return userDetail;
}).then((userDetail) => {
return resolve(userDetail.id);
});
});
}
function createUser(nickName) {
return new Promise((resolve, reject) => {
let sql = "insert into user (name) values (?)";
db_connection.query(sql, [nickName], function (err, userDetail) {
if (err) {
return reject(err);
}
return resolve(userDetail);
});
});
}

MYSQL UPDATE TEXT field not working

I'm trying to update a TEXT field in a MYSQL table. I can't get the UPDATE query to work, even though the original INSERT attempt works fine when the user doesn't exist.
The purpose of the function is to INSERT new row if the user doesn't exist yet, and to UPDATE the textColumn (that always contains a JSON) if the user already exists in the table.
My code:
let first = 'jake';
let last = 'mcdonald';
let first_json = JSON.stringify({a:7, b:7});
let second_json = JSON.stringify({a:'updated'});
const row = {
name: first,
last_name: last,
textColumn: first_json,
}
db.query(`SELECT * FROM tableName
WHERE name="${first}"
AND last_name="${last}"`, (err, result) => {
if (err) console.log(err);
if (result.length < 1) {
db.query(`INSERT INTO tableName SET ?`, row, (err, result) => {
console.log('NEW ROW CREATED: ', result);
})
} else if (result.length > 0) {
console.log('ROW EXISTS');
db.query(`UPDATE tableName
SET textColumn=${second_json}
WHERE name="${first}"
AND last_name="${last}"`, (err, result) => {
console.log('UPDATED: ', result);
}
)
}
}
)
the else if section is what is giving me issues: I reach the inside console.log('ROW EXISTS') but the Update query logs "undefined".
The same UPDATE query works if I try to UPDATE tableName SET name="someNewName" WHERE last_name="original_last_name", but nothing happens when I try to UPDATE the textColumn.