how to query mysql database with where clause using dynamic array - mysql

I'm trying to query mysql database with where clause as below.
Here popularTopicsNames is a dynamic array and its length and elements varies.
var scoreQuery=connection.query('SELECT * FROM LEADERBOARD WHERE SUBTOPIC IN ('+popularTopicsNames+')', function(err,result,fields){
if(err) throw err;
else{
console.log(result);
}

If it's just an array then you're trying to pass it as a string, which won't work.
Try
var scoreQuery = connection.query("SELECT * FROM LEADERBOARD WHERE SUBTOPIC IN ('" + popularTopicsNames.map(mysql.escape).join("','") + "')",
function(err, result, fields) {
if (err) {
throw err;
} else {
console.log(result);
}
});

This will solve the issue in a more readable way.
var scoreQuery = connection.query(`SELECT * FROM LEADERBOARD WHERE SUBTOPIC IN (?)`, [popularTopicsNames],
(err, result, _fields) => {
if (err) {
throw err;
} else {
console.log(result);
}
});

Related

Query not working using node.js node-mysql

Whats wrong with my query? im having this error:
con.query(
'SELECT nick FROM channels WHERE room=1room',
function(err, rows) {
if (err) throw err;
console.log(rows);
}
);
I tried this, and i have the same error:
var room = "1room";
con.query(
'SELECT nick FROM channels WHERE room=' + room,
function(err, rows) {
if (err) throw err;
console.log(rows);
}
);
It is treating 1room as a variable, not a string. Wrap it in quotes and it should work.
con.query(
'SELECT nick FROM channels WHERE room="1room"',
function(err, rows) {
if (err) throw err;
console.log(rows);
}
);
For your second example, you should get in the habit of escaping the variables you use in queries for security reasons (to prevent SQL injection).
var room = "1room";
con.query(
'SELECT nick FROM channels WHERE room=?',
[room],
function(err, rows) {
if (err) throw err;
console.log(rows);
}
);

Node JS Inserting array of objects to mysql database when using transactions

Am using node-mysql to add records to a database but am facing a challenge when the records to be inserted are an array of objects and I need the operation to be a transaction. I have simplified my problem by creating a test project to better explain my problem.
Lets say I have to tables users and orders and the data to be inserted looks like this
var user = {
name: "Dennis Wanyonyi",
email: "example#email.com"
};
var orders = [{
order_date: new Date(),
price: 14.99
}, {
order_date: new Date(),
price: 39.99
}];
I want to first insert a user to the database and use the insertId to add the each of the orders for that user. Am using a transaction since in case of an error, I want to rollback the whole process. Here is how I try to insert all the records using node-mysql transactions.
connection.beginTransaction(function(err) {
if (err) { throw err; }
connection.query('INSERT INTO users SET ?', user, function(err, result) {
if (err) {
return connection.rollback(function() {
throw err;
});
}
for (var i = 0; i < orders.length; i++) {
orders[i].user_id = result.insertId;
connection.query('INSERT INTO orders SET ?', orders[i], function(err, result2) {
if (err) {
return connection.rollback(function() {
throw err;
});
}
connection.commit(function(err) {
if (err) {
return connection.rollback(function() {
throw err;
});
}
console.log('success!');
});
});
}
});
});
However I have a problem iterating over the array of orders without having to call connection.commit multiple times within the for loop
I would suggest to construct a simple string for multiple row insert query for orders table in the for loop first and then execute it outside the for loop. Use the for loop to only construct the string. So you can rollback the query whenever you want or on error. By multiple insert query string i mean as follows:
INSERT INTO your_table_name
(column1,column2,column3)
VALUES
(1,2,3),
(4,5,6),
(7,8,9);
You can use Promise.all functionality of Bluebird for this.
var promiseArray = dataArray.map(function(data){
return new BluebirdPromise(function(resolve, reject){
connection.insertData(function(error, response){
if(error) reject(error);
else resolve(response);
}); //This is obviously a mock
});
});
And after this:
BluebirdPromise.all(promiseArray).then(function(result){
//result will be the array of "response"s from resolve(response);
database.commit();
});
This way, you can work all the inserts asyncronously and then use database.commit() only once.
Some kind of task in Node.js are Asynchronous( like I/O , DB and etc..), and there is a lots of LIBS that help to handle it.
but if you want don't use any lib,for iterating an array in JS and use it in an asynchronous functionality its better to implement it as recursive function.
connection.beginTransaction(function(err) {
if (err) {
throw err;
}
connection.query('INSERT INTO users SET ?', user, function(err, result) {
if (err) {
return connection.rollback(function() {
throw err;
});
}
// console.log(result.insertId) --> do any thing if need with inserted ID
var insertOrder = function(nextId) {
console.log(nextId);
if ((orders.length - 1) < nextId) {
connection.commit(function(err) {
if (err) {
return connection.rollback(function() {
throw err;
})
}
console.log(" ok");
});
} else {
console.log(orders[nextId]);
connection.query('INSERT INTO orders SET ?', orders[nextId], function(err, result2) {
if (err) {
return connection.rollback(function() {
throw err;
});
}
insertOrder(nextId + 1);
});
}
}
insertOrder(0);
});
});
as you can see I rewrite your for loop as a recursive function inside.
I would use the async.each to do the iteration and to fire all the queries in parallel. If some of the queries will fail, the asyncCallback will be called with an error and the program will stop processing the queries. This will indicate that we should stop executing queries and rollback. If there is no error we can call the commit.
I' ve decoupled the code a bit more and split it into functions:
function rollback(connection, err) {
connection.rollback(function () {
throw err;
});
}
function commit(connection) {
connection.commit(function (err) {
if (err) {
rollback(connection, err);
}
console.log('success!');
});
}
function insertUser(user, callback) {
connection.query('INSERT INTO users SET ?', user, function (err, result) {
return callback(err, result);
});
}
function insertOrders(orders, userId, callback) {
async.each(orders, function (order, asyncCallback) {
order.user_id = userId;
connection.query('INSERT INTO orders SET ?', order, function (err, data) {
return asyncCallback(err, data);
});
}, function (err) {
if (err) {
// One of the iterations above produced an error.
// All processing will stop and we have to rollback.
return callback(err);
}
// Return without errors
return callback();
});
}
connection.beginTransaction(function (err) {
if (err) {
throw err;
}
insertUser(user, function (err, result) {
if (err) {
rollback(connection, err);
}
insertOrders(orders, result.insertId, function (err, data) {
if (err) {
rollback(connection, err);
} else {
commit(connection);
}
});
});
});
you need to use async library for these kind of operation.
connection.beginTransaction(function(err) {
if (err) { throw err; }
async.waterfall([
function(cb){
createUser(userDetail, function(err, data){
if(err) return cb(err);
cb(null, data.userId);
});
},
function(userid,cb){
createOrderForUser(userid,orders, function() {
if(err) return cb(err);
cb(null);
});
}
], function(err){
if (err)
retrun connection.rollback(function() {
throw err;
});
connection.commit(function(err) {
if (err) {
return connection.rollback(function() {
throw err;
});
}
console.log('success!');
});
});
});
var createUser = function(userdetail, cb){
//-- Creation of Orders
};
var createOrderForUser = function (userId, orders, cb) {
async.each(orders, function(order, callback){
//-- create orders for users
},function(err){
// doing err checking.
cb();
});
};
See if you can write a Stored Procedure to encapsulate the queries, and have START TRANSACTION ... COMMIT in the SP.
The tricky part comes with needing to pass a list of things into the SP, since there is no "array" mechanism. One way to achieve this is to have a commalist (or use some other delimiter), then use a loop to pick apart the list.
currentLogs = [
{ socket_id: 'Server', message: 'Socketio online', data: 'Port 3333', logged: '2014-05-14 14:41:11' },
{ socket_id: 'Server', message: 'Waiting for Pi to connect...', data: 'Port: 8082', logged: '2014-05-14 14:41:11' }
];
console.warn(currentLogs.map(logs=>[ logs.socket_id , logs.message , logs.data , logs.logged ]));

NodeJS / MySQL return results from a query to variable

I use NodeJS to insert data to a table with many to many relationship, and I want to include two foreign keys when I insert data into the table this is how my code looks:
con.query("SELECT * FROM Transaction WHERE TransactionID > 1", function(err, res) {
if (err) {
throw (err);
} else if (res.length > 0) {
console.log("Transaction already exit");
} else {
var transactionID;
var filePK;
con.query("SELECT Filename FROM File WHERE Filename = ?", fileName, function(err, res) {
if (err) throw err;
filePK = JSON.stringify(res);
});
con.query("SELECT TransactionDescriptionPK FROM TransactionDescription WHERE TransactionDescriptionPK > 0", function(err, res) {
if (err) throw err;
//console.log(res);
transactionID = res;
});
var tran = {
TransactionID: data.ID,
TransactionDate: data.Description,
Amount: data.Amount
};
con.query("INSERT INTO Transaction SET ?", tran, function(err, res) {
if (err) throw err;
});
How I return the results from those queries so I can add them to the tran object?
You can use async.parallel
async.parallel([
function(callback) {
// you can directly pass the parallel callback to mysql query
// if you don't need to do anything else
return con.query(
"SELECT * FROM Transaction WHERE TransactionID > 1",
callback
);
},
function(callback) {
// otherwise, just do you what you want (here JSON.stringify)
// then don't forget to return the parallel callback
return con.query(
"SELECT Filename FROM File WHERE Filename = ?",
fileName,
function(err, res) {
if (err)
return callback(err);
const filePK = JSON.stringify(res);
return callback(null, filePK);
}
);
},
function(callback) {
return con.query(
"SELECT TransactionDescriptionPK FROM TransactionDescription WHERE TransactionDescriptionPK > 0",
callback
);
},
], function(err, data) {
// if any mysql queries above encounter an error,
// it calls the parallel final callback and stops other functions
// without errors, data looks like :
// data[0] equals mysql result object of the first query
// data[1] equals filePK const
// data[2] equals mysql result object of the last query
const tran = {
TransactionID: data[0][0].ID,
TransactionDate: data[0][0].Description,
// Amount: data.Amount // don't know where the amount come from,
// but you get the idea
};
con.query(
"INSERT INTO Transaction SET ?",
tran,
function(err, res) {
if (err)
throw err;
// ...
}
);
});

Nodejs with Mysql Database Return value

I have the following code. I am relative new to nodejs &js
I want to get values in 1. log but i get undefined.
Only 2. log is outputed to the log.
I read nodeJS return value from callback and
https://github.com/felixge/node-mysql but there is no example about return value.
I donot know how to use return statement with the given example in node-mysql page.
exports.location_internal = function (req, res) {
var r = getExternalLocation(2);
// 1. log
console.log(r);
res.send( r);
}
var getExternalLocation = function (id) {
pool.getConnection(function(err, connection){
if(err) throw err;
var response = {};
connection.query( "select * from external_geo_units where geo_unit_id = "+id, function(err, rows){
if(err) throw err;
response.data= rows;
// 2. log
console.log(response);
return response;
});
connection.release();
});
};
It's asynchronous, so you have to pass in a callback to get the value when it's ready. Example:
exports.location_internal = function(req, res, next) {
getExternalLocation(2, function(err, rows) {
if (err)
return next(err);
console.log(rows);
res.send(rows);
});
};
function getExternalLocation(id, cb) {
pool.getConnection(function(err, conn) {
if (err)
return cb(err);
conn.query("select * from external_geo_units where geo_unit_id = ?",
[id],
function(err, rows) {
conn.release();
cb(err, rows);
});
});
}

Node.js mysql transaction

Can anyone provide an example of how I could achieve MySQL transactions in Node.js. I am trying to get my head around using the node-mysql driver and node-mysql-queue.
As far are I can tell, using node-mysql-queue greatly reduces the asynchronous nature of Node.js as new queries have to wait until existing ones have completed. To get around this, has anyone attempted to combine node-mysql-queue with node-mysql's connection-pooling capabilities. i.e starting a new mysql connection for each new http request, and starting transaction queues on individual connections?
Update
See the edit below for async/await syntax
I spent some time writing a generalized version of the transaction example given by node mysql, so I thought I would share it here. I am using Bluebird as my promise library, and used it to 'promisify' the connection object which simplified the asynchronous logic a lot.
const Promise = ('bluebird');
const mysql = ('mysql');
/**
* Run multiple queries on the database using a transaction. A list of SQL queries
* should be provided, along with a list of values to inject into the queries.
* #param {array} queries An array of mysql queries. These can contain `?`s
* which will be replaced with values in `queryValues`.
* #param {array} queryValues An array of arrays that is the same length as `queries`.
* Each array in `queryValues` should contain values to
* replace the `?`s in the corresponding query in `queries`.
* If a query has no `?`s, an empty array should be provided.
* #return {Promise} A Promise that is fulfilled with an array of the
* results of the passed in queries. The results in the
* returned array are at respective positions to the
* provided queries.
*/
function transaction(queries, queryValues) {
if (queries.length !== queryValues.length) {
return Promise.reject(
'Number of provided queries did not match the number of provided query values arrays'
)
}
const connection = mysql.createConnection(databaseConfigs);
Promise.promisifyAll(connection);
return connection.connectAsync()
.then(connection.beginTransactionAsync())
.then(() => {
const queryPromises = [];
queries.forEach((query, index) => {
queryPromises.push(connection.queryAsync(query, queryValues[index]));
});
return Promise.all(queryPromises);
})
.then(results => {
return connection.commitAsync()
.then(connection.endAsync())
.then(() => {
return results;
});
})
.catch(err => {
return connection.rollbackAsync()
.then(connection.endAsync())
.then(() => {
return Promise.reject(err);
});
});
}
If you wanted to use pooling as you suggested in the question, you could easily switch the createConnection line with myPool.getConnection(...), and switch the connection.end lines with connection.release().
Edit
I made another iteration of the code using the mysql2 library (same api as mysql but with promise support) and the new async/await operators. Here is that
const mysql = require('mysql2/promise')
/** See documentation from original answer */
async function transaction(queries, queryValues) {
if (queries.length !== queryValues.length) {
return Promise.reject(
'Number of provided queries did not match the number of provided query values arrays'
)
}
const connection = await mysql.createConnection(databaseConfigs)
try {
await connection.beginTransaction()
const queryPromises = []
queries.forEach((query, index) => {
queryPromises.push(connection.query(query, queryValues[index]))
})
const results = await Promise.all(queryPromises)
await connection.commit()
await connection.end()
return results
} catch (err) {
await connection.rollback()
await connection.end()
return Promise.reject(err)
}
}
The following transaction example was added to the documentation a month ago:
https://github.com/felixge/node-mysql#transactions
connection.beginTransaction(function(err) {
if (err) { throw err; }
connection.query('INSERT INTO posts SET title=?', title, function(err, result) {
if (err) {
connection.rollback(function() {
throw err;
});
}
var log = 'Post ' + result.insertId + ' added';
connection.query('INSERT INTO log SET data=?', log, function(err, result) {
if (err) {
connection.rollback(function() {
throw err;
});
}
connection.commit(function(err) {
if (err) {
connection.rollback(function() {
throw err;
});
}
console.log('success!');
});
});
});
});
I am using the following approach. There is an add function in my Model where I am performing database operations.
add : function (data, callback) {
//Begin transaction
connection.beginTransaction(function(err) {
if (err) {
throw err;
}
var user_query = "INSERT INTO `calldata`.`users` (`username`, `password`, `enabled`, `accountNonExpired`, `accountNonLocked`, `credentialsNonExpired`) VALUES ('" + data.mobile + "', '" + sha1(data.password) + "', '1', '1', '1', '1')";
connection.query(user_query, function(err, results) {
if (err) {
return connection.rollback(function() {
throw err;
});
}
var accnt_dtls_query = "INSERT INTO `calldata`.`accnt_dtls` (`req_mob_nmbr`, `usr_nme`, `dvce_id`, `mngr_id`, `cmpny_id`, `actve_flg`, `crtd_on`, `usr`) VALUES (" + data.mobile + ", '" + data.name + "', '', " + data.managerId + ", " + data.companyId + ", 1, now(), '" + data.mobile+ "')";
connection.query(accnt_dtls_query, function(err, results) {
if (err) {
return connection.rollback(function() {
throw err;
});
}
var user_role_query = "INSERT INTO `calldata`.`user_roles` (`username`, `ROLE`) VALUES ('" + data.mobile + "', '" + data.role + "')";
connection.query(user_role_query, function(err, result) {
if (err) {
return connection.rollback(function() {
throw err;
});
}
//add an entry to manager table
var mngr_dtls_query = "INSERT INTO `calldata`.`mngr_dtls` (`mngr_nm`, `cmpny_id`, `crtd_on`, `usr_nm`, `eml_id`) VALUES ('" + data.name + "'," + data.companyId + " , now(), '" + data.mobile + "', '" + data.mobile + "')";
connection.query(mngr_dtls_query, function(err, result) {
if (err) {
return connection.rollback(function () {
throw err;
});
}
console.log('Changed ' + result.changedRows + ' results');
connection.commit(function (err) {
console.log('Commiting transaction.....');
if (err) {
return connection.rollback(function () {
throw err;
});
}
console.log('Transaction Complete.');
connection.end();
callback(null, result);
});
});
});
});
});
});
//transaction ends here
}
and calling from controller:
agentAccountModel.add(data, function(err, results) {
if(err)
{
res.status(500);
res.json({
"status": 500,
"message": err
});
}
res.status(200);
res.json({
"status": 200,
"message": "Saved successfully"
});
});
I have come up with a solution using a recursive function.
var sql = 'INSERT INTO logs SET data = ?';
// array of rows to insert
var rows = [[/*first row*/], [/*additional row*/]];
connection.beginTransaction(function (err) {
if (err) {
throw err;
}
var insertEachRow = function () {
var row = rows.shift();
if (! row) {
// Done, now commit
return noMoreRows();
}
connection.query(sql, row, function (err, result) {
if (err) {
connection.rollback(function () {
throw err;
});
}
insertEachRow();
});
};
var noMoreRows = function () {
connection.commit(function (err) {
if (err) {
connection.rollback(function () {
throw err;
});
}
console.log('success!');
});
};
insertEachRow();
});
I found one useful links which uses node js mysql pooling with transaction. Database Connection pooling is always useful. One can check this link
https://github.com/mysqljs/mysql
I created a wrapper ORM type thing for this specific purpose, hope it helps SQl-connecton - pool ORM type helper methods
You can use it with the mysql also, just use this function without any parameters /call back .
Make sure you have async function and use these instead
await con.rollback();
await con.beginTransaction();
await con.commit();
Life set.....