I have written a function and exported it using exports. Inside the function I have 2 nested queries. When I have only 1 query I am able to view the values of result. But, after adding the outer query the value of the result from the outer query is undefined.
Here is the code:
main.js
var new1 = function () {
connection.query("select ID from tbl1", function (error, result, fields) {
console.log(result) // This is displayed as undefined.
for (var id in result) {
connection.query("select name from tbl2 where ID = '" + result[id].ID + "' ", function (err, result, fields) {
if (err) throw err
for (var count in result) {
console.log(result[count].name)
}
})
}
})
}
export.new1 = new1;
and in the app.js:
var new2 = require('./main');
new2.new1();
Thank you.
I was going to edit and fix your indentation, but then I saw you are missing a closing quote for the string on the second line: "select ID from tbl1
Now someone else have edited your code and added a closing quote
Related
In the code
var stuff_i_want = '';
stuff_i_want = get_info(parm);
And the function get_info:
get_info(data){
var sql = "SELECT a from b where info = data"
connection.query(sql, function(err, results){
if (err){
throw err;
}
console.log(results[0].objid); // good
stuff_i_want = results[0].objid; // Scope is larger than function
console.log(stuff_i_want); // Yep. Value assigned..
}
in the larger scope
stuff_i_want = null
What am i missing regarding returning mysql data and assigning it to a variable?
============ New code per Alex suggestion
var parent_id = '';
get_info(data, cb){
var sql = "SELECT a from b where info = data"
connection.query(sql, function(err, results){
if (err){
throw err;
}
return cb(results[0].objid); // Scope is larger than function
}
==== New Code in Use
get_data(parent_recording, function(result){
parent_id = result;
console.log("Parent ID: " + parent_id); // Data is delivered
});
However
console.log("Parent ID: " + parent_id);
In the scope outside the function parent_id is null
You're going to need to get your head around asynchronous calls and callbacks with javascript, this isn't C#, PHP, etc...
Here's an example using your code:
function get_info(data, callback){
var sql = "SELECT a from b where info = data";
connection.query(sql, function(err, results){
if (err){
throw err;
}
console.log(results[0].objid); // good
stuff_i_want = results[0].objid; // Scope is larger than function
return callback(results[0].objid);
})
}
//usage
var stuff_i_want = '';
get_info(parm, function(result){
stuff_i_want = result;
//rest of your code goes in here
});
When you call get_info this, in turn, calls connection.query, which takes a callback (that's what function(err, results) is
The scope is then passed to this callback, and so on.
Welcome to javascript callback hell...
It's easy when you get the hang of it, just takes a bit of getting used to, coming from something like C#
I guess what you really want to do here is returning a Promise object with the results. This way you can deal with the async operation of retrieving data from the DBMS: when you have the results, you make use of the Promise resolve function to somehow "return the value" / "resolve the promise".
Here's an example:
getEmployeeNames = function(){
return new Promise(function(resolve, reject){
connection.query(
"SELECT Name, Surname FROM Employee",
function(err, rows){
if(rows === undefined){
reject(new Error("Error rows is undefined"));
}else{
resolve(rows);
}
}
)}
)}
On the caller side, you use the then function to manage fulfillment, and the catch function to manage rejection.
Here's an example that makes use of the code above:
getEmployeeNames()
.then(function(results){
render(results)
})
.catch(function(err){
console.log("Promise rejection error: "+err);
})
At this point you can set up the view for your results (which are indeed returned as an array of objects):
render = function(results){ for (var i in results) console.log(results[i].Name) }
Edit
I'm adding a basic example on how to return HTML content with the results, which is a more typical scenario for Node. Just use the then function of the promise to set the HTTP response, and open your browser at http://localhost:3001
require('http').createServer( function(req, res){
if(req.method == 'GET'){
if(req.url == '/'){
res.setHeader('Content-type', 'text/html');
getEmployeeNames()
.then(function(results){
html = "<h2>"+results.length+" employees found</h2>"
html += "<ul>"
for (var i in results) html += "<li>" + results[i].Name + " " +results[i].Surname + "</li>";
html += "</ul>"
res.end(html);
})
.catch(function(err){
console.log("Promise rejection error: "+err);
res.end("<h1>ERROR</h1>")
})
}
}
}).listen(3001)
Five years later, I understand asynchronous operations much better.
Also with the new syntax of async/await in ES6 I refactored this particular piece of code:
const mysql = require('mysql2') // built-in promise functionality
const DB = process.env.DATABASE
const conn = mysql.createConnection(DB)
async function getInfo(data){
var sql = "SELECT a from b where info = data"
const results = await conn.promise().query(sql)
return results[0]
}
module.exports = {
getInfo
}
Then, where ever I need this data, I would wrap it in an async function, invoke getInfo(data) and use the results as needed.
This was a situation where I was inserting new records to a child table and needed the prent record key, based only on a name.
This was a good example of understanding the asynchronous nature of node.
I needed to wrap the all the code affecting the child records inside the call to find the parent record id.
I was approaching this from a sequential (PHP, JAVA) perspective, which was all wrong.
Easier if you send in a promise to be resolved
e.g
function get_info(data, promise){
var sql = "SELECT a from b where info = data";
connection.query(sql, function(err, results){
if (err){
throw err;
}
console.log(results[0].objid); // good
stuff_i_want = results[0].objid; // Scope is larger than function
promise.resolve(results[0].objid);
}
}
This way Node.js will stay fast because it's busy doing other things while your promise is waiting to be resolved
I've been working on this goal since few weeks, without any result, and I finally found a way to assign in a variable the result of any mysql query using await/async and promises.
You don't need to understand promises in order to use it, eh, I don't know how to use promises neither anyway
I'm doing it using a Model class for my database like this :
class DB {
constructor(db) {
this.db = db;
}
async getUsers() {
let query = "SELECT * FROM asimov_users";
return this.doQuery(query)
}
async getUserById(array) {
let query = "SELECT * FROM asimov_users WHERE id = ?";
return this.doQueryParams(query, array);
}
// CORE FUNCTIONS DON'T TOUCH
async doQuery(queryToDo) {
let pro = new Promise((resolve,reject) => {
let query = queryToDo;
this.db.query(query, function (err, result) {
if (err) throw err; // GESTION D'ERREURS
resolve(result);
});
})
return pro.then((val) => {
return val;
})
}
async doQueryParams(queryToDo, array) {
let pro = new Promise((resolve,reject) => {
let query = queryToDo;
this.db.query(query, array, function (err, result) {
if (err) throw err; // GESTION D'ERREURS
resolve(result);
});
})
return pro.then((val) => {
return val;
})
}
}
Then, you need to instantiate your class by passing in parameter to constructor the connection variable given by mysql. After this, all you need to do is calling one of your class methods with an await before. With this, you can chain queries without worrying of scopes.
Example :
connection.connect(function(err) {
if (err) throw err;
let DBModel = new DB(connection);
(async function() {
let oneUser = await DBModel.getUserById([1]);
let allUsers = await DBModel.getUsers();
res.render("index.ejs", {oneUser : oneUser, allUsers : allUsers});
})();
});
Notes :
if you need to do another query, you just have to write a new method in your class and calling it in your code with an await inside an async function, just copy/paste a method and modify it
there are two "core functions" in the class, doQuery and doQueryParams, the first one only takes a string as a parameter which basically is your mysql query. The second one is used for parameters in your query, it takes an array of values.
it's relevant to notice that the return value of your methods will always be an array of objects, it means that you'll have to do var[0] if you do a query which returns only one row. In case of multiple rows, just loop on it.
var prefix = `SELECT S_PREFIX FROM ServerConfigs WHERE S_ID = ${message.guild.id}`;
connection.query(prefix, function (err, result, fields) {
if (err) return console.log(err);
let res = JSON.parse(JSON.stringify(result[0].S_PREFIX));
const serverPREFIX = res;
It works but sometimes it writes me an error to console.
The error looks like this:
TypeError: Cannot read property 'S_PREFIX' of undefined
Could anybody help me please? Just started learning MYSQL.
result[0] is undefined, which means that your SQL query returned no results, meaning that there were no rows with the S_ID being message.guild.id. Just check that the result array is not empty.
var prefix = `SELECT S_PREFIX FROM ServerConfigs WHERE S_ID = ${message.guild.id}`;
connection.query(prefix, function (err, result, fields) {
if (err) return console.log(err);
if (result.length < 1) {
console.log(`No results for S_ID = ${message.guild.id}`)
return;
}
let res = JSON.parse(JSON.stringify(result[0].S_PREFIX));
const serverPREFIX = res;
Select statements are working fine, but whenever I try an insert or update the recordset and affected values are undefined. The insert/update works in the DB, I just can't read the returned values.
var sql = require('mssql');
var config = {...};
sql.connect(config).then(function() {
new sql.Request().query("INSERT INTO MyTable (Name, Age) VALUES ('John', 30)").then(function(recordset, affected) {
console.log('Recordset: ' + recordset);
console.log('Affected: ' + affected);
}).catch(function(err) {
console.log('Request error: ' + err);
});
}).catch(function(err) {
if (err) {
console.log('SQL Connection Error: ' + err);
}
});
The output to console is:
Recordset: undefined
Affected: undefined
I feel like I must be missing something really simple here.
As mentioned in the comments, INSERT statement doesn't return a recordset so recordset is undefined. Please see this section of the docs to learn more about how to get number of affected rows.
The problem with your code is you're expecting affected as a second argument from the promise, but promises does only support one argument. Because of that you must access number of affected rows this way:
var sql = require('mssql');
var config = {...};
sql.connect(config).then(function() {
var request = new sql.Request();
request.query("INSERT INTO MyTable (Name, Age) VALUES ('John', 30)").then(function(recordset) {
console.log('Recordset: ' + recordset);
console.log('Affected: ' + request.rowsAffected);
}).catch(function(err) {
console.log('Request error: ' + err);
});
}).catch(function(err) {
if (err) {
console.log('SQL Connection Error: ' + err);
}
});
If you want id to be output parameter
const sql = require("mssql/msnodesqlv8");
const pool = new sql.ConnectionPool(dbConfig);`
const poolConnect = pool.connect();
let query = `INSERT INTO <table>(fields) VALUES(values);SELECT #id = SCOPE_IDENTITY()`
await poolConnect;
pool.request()
.output("id", sql.Int)
.query(query).then((err, result) => {
console.log(result.output.id)
}).catch(err => {
console.log(err)
})`
This has to be a scope issue that I'm not familiar with. I have a small module I've written as so:
(function () {
var getPlanInfo = function (id, conn) {
conn.query('SELECT * FROM `items` WHERE `id` = ?', [id], function (error, result) {
if (error) console.error('Query error: ' + error.stack);
console.log(result[0]); // Everything is great
return result[0];
});
};
modules.exports.getPlanInfo = function (id, conn) { return getPlanInfo(id, conn); // Typo }
})();
Here comes the problem. When I call it from anywhere (inside the module itself or another file), the return value is always undefined. I checked from within the function, the query returns the result as expected.
var backend = require('./module.js');
var t = backend.getPlanInfo();
t is undefined. This is the same if I call that method from inside the module itself (another function within that module).
I'm familiar with the callback principle in javascript and how objects and functions have to be passed around as an argument to remain in scope. Is this the issue here or is this a node.js particularity?
I tried in in the Developer Console (Chrome), works as expected.
conn.query() looks like it is async. Thus, you can't return its result from getPlanInfo() because getPlanInfo() returns long before the result is available. Returning result[0] from the conn.query() callback just returns a piece of data back into the conn.query() infrastructure. getPlanInfo() has long before already returned.
If you want an async result, then you will have to change getPlanInfo() to use a mechanism that supports getting async results such as a direct callback or a promise or something like that.
Here's a plain callback way:
var getPlanInfo = function (id, conn, callback) {
conn.query('SELECT * FROM `items` WHERE `id` = ?', [id], function (error, result) {
if (error) {
console.error('Query error: ' + error.stack);
callback(error);
return;
}
console.log(result[0]); // Everything is great
callback(0, result[0]);
});
};
modules.exports.getPlanInfo = getPlanInfo;
Then, the caller of that module would look like this:
var m = require('whatever');
m.getPlanInfo(id, conn, function(err, result) {
if (err) {
// error here
} else {
// process result here
}
});
You don't return anything from getPlanInfo. Probably you wanted to write modules.exports.getPlanInfo = function (id, conn) { return getPlanInfo; }
(with return getPlanInfo; instead of return getPlanInfo();)
I have the following piece of javascript but for some scoping reason the "names" that is returned from myfunc is empty.
var myfunc = function(client, id) {
var names = new Array();
client.query(
'SELECT stuff FROM mytable WHERE id="'+id+'"',
(function selectCb(err, results, fields) {
if (err) {
throw err;
}
for (result in results) {
// This prints fine
console.log(results[result].name);
names[result] = results[result].name;
}
client.end();
})
);
// The following returns empty
return names;
}
console.log(myfunc(1,2));
How can I make it break out of scope?
It's empty because the call to your "query" function is asynchronous. The function you pass into it won't be run until the results are available. Therefore, your "myfunc" function returns immediately, long before that callback function is invoked.
Using Javascript in a browser, you have to think in those terms. Instead of expecting your "names" to be ready immediately, change "myfunc" so that you pass it a callback function to be invoked when the names are actually available:
var myfunc = function(client, id, whenFinished) {
var names = new Array();
client.query(
'SELECT stuff FROM mytable WHERE id="'+id+'"',
(function selectCb(err, results, fields) {
if (err) {
throw err;
}
for (result in results) {
// This prints fine
console.log(results[result].name);
names[result] = results[result].name;
}
client.end();
if (whenFinished) whenFinished(names); // callback
})
);
};
Now when you call your function, instead of expecting the "names" as a return value, you'll pass in another function that will act on the list of names:
myfunc(1, 2, function(names) { console.log(names); });
If client.query(...) is asynchronous, then the selectCb function would not have run and names would not have changed by the time myfunc returns. You need to redesign myfunc to return names asynchronously (by, for example, accepting a function parameter which it calls at the end of selectCb).
var names = new Array();
var myfunc = function(client, id) {
client.query(
'SELECT stuff FROM mytable WHERE id="'+id+'"',function selectCb(err, results, fields)
{
if (err) {
throw err;
}
for (result in results) {
// This prints fine
console.log(results[result].name);
names[result] = results[result].name;
}
client.end();
}
);
// The following returns empty
return names;
}
console.log(myfunc(1,2));
try making names global