I have been struggling for a few days with a problem that seems simple to me. I want to insert a value if it does not exist in a mysql database with nodejs.
I have an array of array, and I need to loop inside a child to check every data.
Example of data
finalRow = [['a > 'b'], ['a' > 'c']];
My code
for(let i=0; i < finalRow.length-1; i++) {
var splitcat = finalRow[i].split(' > ');
(async function(){
for(let j=0; splitcat.length-1; j++) {
var query = "SELECT * from category where name = '" + splitcat[j] + "'";
await con.query(query, async function (err, result, fields) {
if (err) throw err;
if(result && result.length) {
console.log("nothing");
} else {
console.log("insert into db");
await con.query(`INSERT INTO category (name, url, description, parent_id) VALUES (?, ?, ?, ?)`,
[splitcat[j], '', '', null], function (err, result) {
if (err) throw err;
});
}
});
}
})();
I tried several versions of this code, but
the loop does not wait for the existence check and inserts all the data.
Thanks for your help
Related
When an existing SQL record exists I want to use it rather than adding another, but if it doesn't yet exist I want to add it. The issue I am having is that when my Node.js app's endpoint is called it's not executing in the correct order so the SQL lookup to find existing records is happening after I check it's length to see if I need to add a new record.
// it does this second
let existingGet = "SELECT * FROM Items WHERE name = '" + productName + "'";
let existingItem = async () => {
db.query(existingGet, function (err, rows, fields) {
return rows;
});
};
// it does this first
if (await existingItem().length > 0) {
// Existing found, use existing
itemId = existingItem.ID;
} else {
// Item not found, create new
var sql = "INSERT INTO Items (...) VALUES (...)";
await db.query(sql, async function (err, result) {
itemId = existingItem.affectedRows.ID;
});
}
The desired outcome is that it does the first section before the second section because the second section needs the results of the first.
Try removing the outer brackets so that the existingItem will receive the result from the query
// it does this second
let existingGet = "SELECT * FROM Items WHERE name = '" + productName + "'";
//removed outer brackets
let existingItem = async () =>
db.query(existingGet, function (err, rows, fields) {
return rows;
});
// it does this first
if (await existingItem().length > 0) {
// Existing found, use existing
itemId = existingItem.ID;
} else {
// Item not found, create new
var sql = "INSERT INTO Items (...) VALUES (...)";
await db.query(sql, async function (err, result) {
itemId = existingItem.affectedRows.ID;
});
}
Do it in a single db call using sql command
"INSERT INTO Items (...) VALUES (...)
WHERE NOT EXISTS (SELECT 1 FROM Items WHERE name = ...)"
And use sql command parameters instead of concatenation to avoid sql injection.
Trying to query my database and each time I do, it runs all the queries at the end instead of when I need them in nodejs.
var mysql = require('mysql');
var con = mysql.createConnection({
host: database.host,
user: database.user,
password: database.password,
database: database.database
});
The connection data I am pulling from a json file.
function getSymbol(id){
var s = "";
con.query("SELECT * FROM Symbol WHERE PlayerID = '" + id + "'", function (err, result, fields) {
if (err) throw err;
console.log(result);
if (result.length < 1) {
console.log(result);
s = result[0].Symbol;
}
else {
s = "!";
}
});
console.log(s);
return s;
}
It all runs at the end of the program wondering if there is a fix or if I should switch to python (seriously considering rewriting all of it at this point).
The problem is what you have written comes under NIO and it wont wait for executing the next statement unless you ask it to. Try the below code:
async function getSymbol(id){
var s = "";
try {
let result = await con.query("SELECT * FROM Symbol WHERE PlayerID = '" + id + "'")
if (result.length < 1) {
console.log(result);
s = result[0].Symbol;
}
else {
s = "!";
}
}catch(error){
console.log(error);
throw new error;
}
console.log(s);
return s;
}
Note: I have used async/await. You can also use Promises
As mentioned, the method is NOT sync. Your result will be on the callback you pass.
con.query(sql, function (err, result) {
if (err) throw err;
console.log("The Data: " + result);
});
More Info: https://www.w3schools.com/nodejs/nodejs_mysql.asp
Moreover, you need to connect first with con.connect( <callback-here> ).
The best way to work with this is to avoid the callbacks for async/wait syntax.
so here's the issue, everything inside the con.query(query, function (err, result, fields) is never called. So this is the part of code that is about verifying that when the user is signing up, the email that he took is not already taken. argv[0] contains the email that the user typed (it's not wrong, I debugged it). So I obviously created two accounts with the same email but the second one is never flagged (the function is continuing instead of returning "userexists").
var mysql = require('mysql');
var con = mysql.createConnection(Credentials). // 👈 The connection is established, it's not a problem.
var query = "SELECT id FROM accounts_data WHERE email = '" + argv[0] + "'";
var r;
con.query(query, function (err, result, fields) {
if (err)
return "failed";
if(result != undefined)
return "userexists" // The if is never checked
});
Thank you.
EDIT:
Hello everyone, so the console.log(results.length) is printing the right result, but how can I give the result to r? Because the last console.log(r) is still printing 0.
var r = 0;
var sql = 'SELECT * FROM accounts_data WHERE email = ' + con.escape(argv[0]);
con.query(sql, function (error, results, fields) {
if (error) throw error;
console.log(results.length); // Right result
r = results.length; // Not giving the result to r
});
console.log(r); // Not the right result
Try using Promise's, they'll help get away from 'callback-hell'
const userExists = async emailAddress => new Promise((resolve, reject) => {
con.query(`
SELECT EXISTS(
SELECT id FROM accounts_data WHERE email = ?
);
`,
[emailAddress],
(err, result) => {
if (err) { reject(err); }
resolve(result);
}
)
);
await userExists('abc#example.com') // => boolean
I posted a question before and realized my problem actually was async functions. I managed to work out most of it, but I got one little problem left. Using async I used waterfall to create an order for the some queries...
exports.getMenu = function(id_restaurant, callback){
async.waterfall([
async.apply(firstQuery, id_restaurant),
secondQuery,
thirdQuery,
fourthQuery,
formMenu
], function(err, result){
if(err){
console.log(err);
}
callback(result);
});
};
Everything works until fourthQuery, where I have to loop to get all dishes of a menu.
function fourthQuery(array_totalP, array_nombresSecc, array_secciones, callback){
var size = array_nombresSecc.length;
var array_secciones = array_secciones;
var array_nombresSecc = array_nombresSecc;
var dishes = [];
pool.getConnection(function(err, connection) {
if(err) {
console.log(err);
callback(true);
return;
}
for (var i = 0; i < size; i++) {
connection.query("SELECT name, price FROM menu_product WHERE id_seccion = ? AND active = 1", [array_secciones[i]],
function(err, results2) {
if(err) {
console.log(err);
callback(true);
return;
}
console.log("Result query 4 " + JSON.stringify(results2));
dishes[i] = results2;
console.log("VALOR PLATILLOS EN i : " + JSON.stringify(dishes[i]));
// this prints the result but only if it has a value over 2
});
};
}); // pool
console.log("I'm sending " + dishes); // this logs an empty array
callback(null, dishes, array_nombresSecc);
};
So what i can see that happens from printing the value of 'i' each loop is that it always has the value of 2. Because that's 'size' value. Also, even though it's saving results of index '2' I believe the callback is being done even before the for loop is done, because my fifth function is recieving an empty array.
How can i make my code wait to callback until my for loop is done?
NOTE: Sorry, part of my code is in spanish, tried to translate the important parts of it.
There are a few ways to handle this, one is to look into promise architecture. Promise.all will let you supply one callback to handle the values from each child promise.
To use what you've already got, however, I'd push the values into your dishes array, rather than assigning them specifically to i indexes, then check the size of that array at the end of each connection. When the array length matches the size, fire the callback. (as seen below)
If you need a way to tie each result to that specific i value, I'd recommend pushing them as an object
dishes.push({'index': i, 'dish': results2})
Afterward, if you need the array of just dishes, you can sort the array by that index value and run a map function.
dishes.sort(function(a,b){ return a.index - b.index; })
dishes = dishes.map(function(a){ return a.dish })
Here's the code adjusted:
function fourthQuery(array_totalP, array_nombresSecc, array_secciones, callback) {
var size = array_nombresSecc.length;
var array_secciones = array_secciones;
var array_nombresSecc = array_nombresSecc;
var dishes = [];
pool.getConnection(function(err, connection) {
if (err) {
console.log(err);
callback(true);
return;
}
for (var i = 0; i < size; i++) {
connection.query("SELECT name, price FROM menu_product WHERE id_seccion = ? AND active = 1", [array_secciones[i]],
function(err, results2) {
if (err) {
console.log(err);
callback(true);
return;
}
console.log("Result query 4 " + JSON.stringify(results2));
dishes.push(results2)
if(dishes.length == size){
console.log("I'm sending " + dishes);
callback(null, dishes, array_nombresSecc)
}
console.log("VALOR PLATILLOS EN i : " + JSON.stringify(dishes[i]));
// this prints the result but only if it has a value over 2
});
};
}); // pool
;
};
Since you're already using the async, I would suggest replacing the for() loop in fourthQuery with async.each().
The updated fourthQuery would look like this:
function fourthQuery(array_totalP, array_nombresSecc, array_secciones, callback){
var size = array_nombresSecc.length;
var array_secciones = array_secciones;
var array_nombresSecc = array_nombresSecc;
var dishes = [];
pool.getConnection(function(err, connection) {
if(err) {
console.log(err);
callback(true);
return;
}
async.each(array_secciones,
function(item, itemCallback) {
// Function fun for each item in array_secciones
connection.query("SELECT name, price FROM menu_product WHERE id_seccion = ? AND active = 1", [item],
function(err, results2) {
if(err) {
console.log(err);
return itemCallback(true);
}
console.log("Result query 4 " + JSON.stringify(results2));
dishes.push(results2);
console.log("VALOR PLATILLOS EN i : " + JSON.stringify(dishes[dishes.length-1]));
// this prints the result but only if it has a value over 2
return itemCallback();
});
},
function(err) {
// Function run after all items in array are processed or an error occurs
console.log("I'm sending " + dishes); // this logs an empty array
callback(null, dishes, array_nombresSecc);
});
}); // pool
};
Alternatively, you can use async.map(), which handles gathering the results in the final callback so doesn't rely on the dishes variable.
I have an array of customer objects, that I wish to insert to the SQL database.
The customer objects are retrieved from the req data.
I am using Tedious for the request, and Tedious Connectionpool in order to have multiple connections at the same time.
When looping over the objects i am getting an error when trying to insert, the error being
{ [RequestError: Violation of PRIMARY KEY constraint `'PK__Customer__A4AE64D873A5400C'. Cannot insert duplicate key in object 'dbo.Customer'. The duplicate key value is (2).]`
Note that I only have 3 object being send in the req at this time. It looks to me that it is only the last object that are being handled and inserted. But since I am new to using tedious with Node.js i cant spot my mistake. Any suggestions ?
router.post('/',jsonParser, function(req, res) {
var customers = req.body.customers;
var companies = req.body.companies;
var Connection = require('tedious').Connection;
var Request = require('tedious').Request;
var TYPES = require('tedious').TYPES;
var config = {
userName: '*************',
password: '*************',
server: '***********.database.windows.net',
// When you connect to Azure SQL Database, you need these next options.
options: {encrypt: true, database: '*******'}
};
var poolConfig = {
min: 1,
max: 3,
log: true
};
var pool = new ConnectionPool(poolConfig, config);
for (var i = 0; i < customers.length; i++) {
console.log('Inserting '+customers[i].firstname);
var firstname = customers[i].firstname;
var count = i;
pool.acquire(function (err, connection) {
if (err)
console.error(err);
//use the connection as normal
var request = new Request("INSERT INTO dbo.Customer" +
" (Firstname,CustomerId)" +
"VALUES" +
" (#Firstname,#CustomerId);", function (err, rowCount) {
if (err)
console.error(err);
console.log('rowCount: ' + rowCount);
//release the connection back to the pool when finished
connection.release();
});
request.addParameter('Firstname', TYPES.VarChar,firstname);
request.addParameter('CustomerId', TYPES.Int, count);
request.on('row', function (columns) {
console.log('value: ' + columns[0].value);
});
connection.execSql(request);
});
pool.on('error', function (err) {
console.error(err);
});
}
});
The scope of your variables count and firstName are global. By the time the pool.acquire( function get's executed the for loop has completed and it is inserting the last customer twice. One possible solution would be to put an anonymous function inside the for loop e.g. (it doesn't have to be anonymous though)
for (var i = 0; i < customers.length; i++) {
(function(count, firstName) {
...do insert here...
}(i, customers[i].firstname));
}