nodejs and sql queries - concurrent access - mysql

I'm discovering nodejs and its asynchronous system and I'm doing a query in MySQL with the node-mysql package. I'm doing something like that:
iterating a collection
if the item is not in the DB, I insert
if the item is in the DB, I update
So my code look like that:
var stuff = ['a', 'b', 'a'];
for(var i=0;i<stuff.length;i++){
connection.query("SELECT COUNT(*) AS count FROM table WHERE column = ?", [stuff[i]],
(function(err, rows){
if(rows.count == 0){
connection.query("INSERT INTO table ...");
} else {
connection.query("UPDATE table SET ...");
}
}).bind(this)
}
But I'm wondering, because of asynchronous mode, if sometimes there is a problem with this pattern. What is the behavior here ?
"SELECT WHERE column = 'a'" ==> count == 0
"SELECT WHERE column = 'b'" ==> count == 0
"SELECT WHERE column = 'a'" ==> count == 0
"INSERT 'a'"
"INSERT 'b'"
"INSERT 'a'" ==> unexpected behavior
or
"SELECT WHERE column = 'a'" ==> count == 0
"INSERT 'a'"
"SELECT WHERE column = 'b'" ==> count == 0
"INSERT 'b'"
"SELECT WHERE column = 'a'" ==> count == 1
"UPDATE 'a'" ==> expected behavior !!
I hope you will understand my problem, sorry for my bad english, it's a real handicap..
Thank you.

mysql has a insert or update command, http://dev.mysql.com/doc/refman/5.0/en/insert-on-duplicate.html This will insert if it doesn't exist, or update if it does.
As for insert/update with this async behavior, take a look at the async lib to run things like this in series instead. With your code example above, you can't be sure which will run/finish first IIRC.
It also depends on what exactly you are trying to update. If you are just going to count something, and need to increment the count, order doesn't matter. Again, this all depends on what you actually want to insert/update.

I find that recursion often works great for this kind of challenge. I use it all the time - it seems simpler than resorting to async for such a straight-forward problem.
Something like this:
var stuff = ['a', 'b', 'a'];
processStuff(stuff);
function processStuff(theStuff) {
if (theStuff.length) == 0 {
// Done all items - either return result to client or execute some callback here
// res.end('Completed');
return;
}
var thisItem = theStuff.shift();
connection.query('SELECT * FROM table WHERE column = ' + connection.escape(thisItem), function(err, rows) {
if (rows.length == 0) {
connection.query("INSERT INTO table ...", function(err, result) {
processStuff(theStuff);
});
} else {
connection.query("UPDATE table SET ...", function(err, result) {
processStuff(theStuff);
});
}
});
}
This will keep your queries and updates/inserts flowing one after another, and yet it is still asynchronous. I find this easier to read as well, and easy to modify.

Related

How to build a dynamic SQL "WHERE" statement using a JSON input

I am trying to dynamically build my SQL statement using node. The where clause will be completely different for each of my cases.
const sql = `select columnName from tableName where ?`;
const whereClause = { "name": "Siri", "Age":20}
connection.query(sql, whereClause, (err, rows) { ... });
However, I keep getting SQL syntax error. The query node builds is select columnName from tableName where name = 'siri', age = 20. I figured the reason I get SQL syntax error is because the SQL statement is missing the AND part. I want to be able to construct the query by giving the JSON object for the where clause.
I don't want to build the query using string concatenation due to SQL injection risks. So, is there another way that I can build my SQL statement without manually adding the AND part?
I'm pretty sure you can't process column names like that. Write a helper function that processes the json object and escapes values.
function processValue(value) {
if(!isNaN(value)) {
return value;
}
if(typeof value === "string") {
return `"${mysql.escape(value)}"`;
}
throw new Error("Unsupported value type!");
}
function where(obj) {
return Object.entries(obj).reduce(function(statement, [key, value]) {
return statement.concat(["AND", key, "=", processValue(value)]);
}, []).slice(1).join(" ");
}
Your query now looks like this:
const sql = `select columnName from tableName where ?`;
connection.query(sql, where({ "name": "Siri", "Age":20 }), (err, rows) { ... });
On another note, just use an ORM or a query builder like Knex so that you don't have to do all this manually.

How to check if update mysql table is successful? [duplicate]

When using felixge's mysql for node.js, how can I ask the result object for the number of returned rows? I have a rather expensive query so I don't want to run a COUNT(*) first, just to then run a query a second time.
If it's a select query, just take the length of the returned array.
connection.query(sql, [var1,var2], function(err, results) {
numRows = results.length;
});
If it's an update/delete query, the returned dictionary will have an affectedRows variable.
connection.query(sql, [var1,var2], function(err, result) {
numRows = result.affectedRows;
});
If you're using the examples in the readme just look at the length property of the rows object (i.e. rows.length).
With the version of mssql 2.1.2 as of 2015-04-13:
delete from DeviceAccountLinks
where DeviceAccountId = #deviceAccountId
and DeviceType = #deviceType
statement will produce no results as 'undefined'
I have changed the statement to:
delete from DeviceAccountLinks
where DeviceAccountId = #deviceAccountId
and DeviceType = #deviceType;
select ##rowcount "rowCount"
to get the output of: [{rowCount:1}]

How to get output from NodeRed Mysql node

I want to get out put from NodeRed mysql node.
Here image of connection :
Select Query is :
msg.topic = "SELECT * t.TableID FROM booking t where t.bookingdate='"+formattedDate+"' and t.TableID = 3";
Output i am trying to get:
if(msg.payload.TableID ==3){
var id = "15";
var message = "Front Desk";
msg.topic = "INSERT INTO tableMessage(TableID, MESSAGE) VALUES ('"+id+"' ,'"+message+"')";
return msg;
}
Question is msg.payload.TableID ==3 is it right? is it right way to read out put from Mysql node
Select query are tested working fine.
but this condition not working for me.
any one help me how to retrieve data from mysql node.
Thanks
The output from the mysql node is an array of rows so your test should be something like this:
if (msg.payload[0].TableID ==3) {
...
EDIT:
You can test if no results were returned by testing the content of msg.payload
if (!msg.payload) {
//no results
} else if (msg.payload[0].TableID == 3) {
//results
}

MySQL INSERT IF NOT EXIST else DELETE row

I have a table that links 2 other tables together.
I have a list of checkboxes, where when one is checked, it sends the value to the server via ajax.
What I want to do is if the values are not in the database, insert them (checked) or if they are there, delete them (unchecked)
Is there a way to do it without writing several queries? I know its not to hard with an insert/update, but what about delete?
You can allways delete and, if affected rows is 0, then insert. Easy, simple and it works.
INSERT IF NOT EXISTS ... is not available in MySQL (it is specific to T-SQL afaik). Use a transaction instead but keep in mind that not every MySQL engine supports transactions although no error is raised when using transactional statements.
This might help you out depending on your key structure http://bogdan.org.ua/2007/10/18/mysql-insert-if-not-exists-syntax.html
Otherwise you might be able to handle what you're doing with a subquery?
Not 100% what you are looking for, but it does the insert if not exists:
http://remy.supertext.ch/2010/11/mysqlupdate-and-insert-if-not-exists/
INSERT INTO wp_postmeta (post_id, meta_key) SELECT ?id, ‘page_title’
FROM DUAL WHERE NOT EXISTS (
SELECT meta_id FROM wp_postmeta WHERE post_id = ?id AND meta_key = ‘page_title’);
UPDATE wp_postmeta SET meta_value = ?page_title WHERE post_id = ?id AND meta_key = ‘page_title’;
There is no such thing as ON DUPLICATE DELETE or a correct use of IF ELSE with subqueries to do that in one query (which would be handy since this one query will either succeed or fail). Think you'd have to roll over several queries in achieving your goal, like I did. But keeping track on whether or not the subsequent queries keep succeeding.
Start out with a return value ('state' in this case) which is "Unknown" or "NoChange". If this returns to you ajax call, you know the checkbox needs to return to its previous state before clicking, and possible popup a message (which you can carry along with the json).
run your queries in a correct if/then code structure, and keep track of succeeding queries, and update the 'state' when a series succeeded accordingly.
This should do exactly what you need, keep it from having black holes.
(I use some wrappers for running queries, that return true/false/null/errorinformation - which allows you to check each query result)
I assume you have your ajax call in place already. The code example starts where your ajax call starts processing from the server side:
ob_end_clean();
$json = array("state" => "Unknown");
$module_id = (isset($_POST['module_id']) ? $_POST['module_id'] : null);
$group_id = (isset($_POST['group_id']) ? $_POST['group_id'] : null);
$action = (isset($_POST['action']) ? $_POST['action'] : null);
if (!empty($module_id) && !empty($group_id) && !empty($action)) {
$query = "
SELECT
1
FROM
config_module_actions_groups
WHERE
1
AND module_id = '{$module_id}'
AND group_id = '{$group_id}'
AND action = '{$action}'
";
if (getQueryAsArraySingleValues($query)) {
$query = "
DELETE FROM
config_module_actions_groups
WHERE
1
AND module_id = '{$module_id}'
AND group_id = '{$group_id}'
AND action = '{$action}'
";
if (executeQuery($query)) {
$json["state"] = "Off";
} else {
$json["message"] = "Not correctly deactivated";
}
} else {
$query = "
REPLACE INTO
config_module_actions_groups
SET
module_id = '{$module_id}',
group_id = '{$group_id}',
action = '{$action}'
";
if (executeQuery($query)) {
$json["state"] = "On";
} else {
$json["message"] = "Not correctly activated";
}
}
}
// output
echo json_encode($json);
exit;

Linq to SQL: Queries don't look at pending changes

Follow up to this question. I have the following code:
string[] names = new[] { "Bob", "bob", "BoB" };
using (MyDataContext dataContext = new MyDataContext())
{
foreach (var name in names)
{
string s = name;
if (dataContext.Users.SingleOrDefault(u => u.Name.ToUpper() == s.ToUpper()) == null)
dataContext.Users.InsertOnSubmit(new User { Name = name });
}
dataContext.SubmitChanges();
}
...and it inserts all three names ("Bob", "bob" and "BoB"). If this was Linq-to-Objects, it wouldn't.
Can I make it look at the pending changes as well as what's already in the table?
I don't think that would be possible in general. Imagine you made a query like this:
dataContext.Users.InsertOnSubmit(new User { GroupId = 1 });
var groups = dataContext.Groups.Where(grp => grp.Users.Any());
The database knows nothing about the new user (yet) because the insert wasn't commited yet, so the generated SQL query might not return the Group with Id = 1. The only way the DataContext could take into account the not-yet-submitted insert in cases like this would be to get the whole Groups-Table (and possibly more tables, if they are affected by the query) and perform the query on the client, which is of course undesirable. I guess the L2S designers decided that it would be counterintuitive if some queries took not-yet-committed inserts into account while others wouldn't, so they chose to never take them into account.
Why don't you use something like
foreach (var name in names.Distinct(StringComparer.InvariantCultureIgnoreCase))
to filter out duplicate names before hitting the database?
Why dont you try something like this
foreach (var name in names)
{
string s = name;
if (dataContext.Users.SingleOrDefault(u => u.Name.ToUpper() == s.ToUpper()) == null)
{
dataContext.Users.InsertOnSubmit(new User { Name = name });
break;
}
}
I am sorry, I don't understand LINQ to SQL as much.
But, when I look at the code, it seems you are telling it to insert all the records at once (similar to a transaction) using SubmitChanges and you are trying to check the existence of it from the DB, when the records are not inserted at all.
EDIT: Try putting the SubmitChanges inside the loop and see that the code will run as per your expectation.
You can query the appropriate ChangeSet collection, such as
if(
dataContext.Users.
Union(dataContext.GetChangeSet().Inserts).
Except(dataContext.GetChangeSet().Deletes).
SingleOrDefault(u => u.Name.ToUpper() == s.ToUpper()) == null)
This will create a union of the values in the Users table and the pending Inserts, and will exclude pending deletes.
Of course, you might want to create a changeSet variable to prevent multiple calls to the GetChangeSet function, and you may need to appropriately cast the object in the collection to the appropriate type. In the Inserts and Deletes collections, you may want to filter it with something like
...GetChangeSet().Inserts.Where(o => o.GetType() == typeof(User)).OfType<User>()...