Node.js / SQL: Insert only new values - mysql

I have a small DB where i insert new data to each column at a different time. Because I'm only inserting one new value, the values for other columns will become NULL. But if thats the case, i want to keep the old value.
My DB looks like this:
One solution would be using coalesce() i guess, but i'm updating each column dynamically, and so the other column names are unknown.
function database_call(request) {
database.query(request, function (err, result) {
if (err) {
console.log(err);
}
});
}
subscribedItem.on("changed", function (dataValue) {
let databaseAttribute = subscribedItem.itemToMonitor.nodeId.value;
let databaseValue = dataValue.value.value;
databaseAttribute = databaseAttribute.substring(databaseAttribute.indexOf('.')+1)
databaseAttribute = databaseAttribute.replace(".", '');
databaseAttribute = databaseAttribute.replace(/"/g, '');
database_call("INSERT INTO Prozessdaten ("+databaseAttribute+") VALUES ("+databaseValue+")");
});

I've found this that implements a 'vertical' coalesce.
You should first do a query like this, using SUBSTRING_INDEX and GROUP_CONCAT to obtain the latest not-null value available in the database for each column.
SELECT
SUBSTRING_INDEX(GROUP_CONCAT(05Hz ORDER BY ID DESC SEPARATOR '##INDEX##'), '##INDEX##', 1) AS 05Hz,
SUBSTRING_INDEX(GROUP_CONCAT(5Hz ORDER BY updated_at DESC SEPARATOR '##INDEX##'), '##INDEX##', 1) AS 5Hz
FROM
table
LIMIT 1
After that, update the single value you really need to update and perform an insert specifying all the values for every column.

Related

Yii2 mysql how to insert a record into a table where column value already exists and should be unique?

I have a table, say 'mytable' that use a "rank" column that is unique. After having created some record where rank is successively rec A(rank=0), rec B (rank=1), rec C (rank=2), rec D (rank=3), rec E (rank=4).
I need to insert a new record that will take an existing rank, say 1, and modify the rank value of the following records accordingly.
The result being : rec A(rank=0), new rec (rank=1), rec B (rank=2), rec C (rank=3), rec D (rank=4), rec E (rank=5).
How can I do this ? Can this be solved with mysql only or should I write some important bunch of code in PHP (Yii2) ?
Assuming no rank is skipped you need to shift existing ranks before saving the new record. To do that you can use beforeSave() method of your ActiveRecord like this:
class MyModel extends \yii\db\ActiveRecord
{
public function beforeSave($insert)
{
if (!parent::beforeSave($insert)) {
return false;
}
if ($insert) { //only if we are saving new record
{
$query = self::find()
->where(['rank' => $this->rank]);
if ($query->exists()) { //check if the rank is already present in DB
//we will create the query directly because yii2
// doesn't support order by for update
$command = static::getDb()->createCommand(
"UPDATE " . static::tableName() .
" SET rank = rank + 1 WHERE rank >= :rank ORDER BY rank DESC",
[':rank' => $this->rank]
);
$command->execute();
}
}
return true;
}
// ... other content of your model ...
}
MySQL allows use of ORDER BY in UPDATE query, that will help us deal with fact that doing UPDATE on table is not atomic and the UNIQUE constraint is checked after each row is updated.
It would be more problematic if there are skipped ranks. In that case you will need to shift ranks only until you hit first skipped rank.
Another option might be creating an before insert trigger on the table that would do the rank shifting.
Note:
It might be a good idea to also implement afterDelete method to shift the ranks in oposite direction when some record is removed to avoid skipped ranks.
Resources:
\yii\db\BaseActiveRecord::beforeSave()
\yii\db\ActiveRecord::updateAllCounters() - replaced with direct update
MySQL triggers
MySQL UPDATE syntax

MySQL - insert into... on duplicate key update - How to distinguish between insert or update?

I am using Node.js. I use mysql and bluebird packages.
const pool = mysql.createPool({ ... });
const query = (stmt, params) => {
return Promise.promisify(pool.query, { context: pool })(stmt, params);
};
const params = { ... };
const stmt = 'insert into table set ? on duplicate key update ?';
return query(stmt, [params, params])
.then(results => {
// I want to know actually what is done, insert or update
});
There should be a key affectedRows from the return object. From the reference, affectedRows will be 1 if it is inserted, and 0 or 2 if it is updated.
return query(stmt, [params, params])
.then(results => {
// I want to know actually what is done, insert or update
if (results.affectedRows === 1) {
// inserted
} else {
// updated
}
});
For INSERT ... ON DUPLICATE KEY UPDATE statements, the affected-rows value per row is 1 if the row is inserted as a new row, 2 if an existing row is updated, and 0 if an existing row is set to its current values. If you specify the CLIENT_FOUND_ROWS flag, the affected-rows value is 1 (not 0) if an existing row is set to its current values.
Reference: https://dev.mysql.com/doc/refman/8.0/en/mysql-affected-rows.html
While I'm not as savvy with this bit of node api, the basics are the same:
query(stmt, [params.col1, params.col2])
.then(results => {
Console.log("Affected rows: " + results.affectedRows);
});
Now, the real problem is that MySQL is a fan of essentially returning garbage diagnostic information. If you modify more than 1 row, you'll have no idea what occurred on each one, thanks to this:
With ON DUPLICATE KEY UPDATE, the affected-rows value per row is 1 if the row is inserted as a new row, 2 if an existing row is updated, and 0 if an existing row is set to its current values.
If you can afford it, do these statements one at a time, and check the affected row count. Otherwise, I'm digging through some MySQL internal functions but I'm not seeing much.
As a side note, you're overreaching with your wildcards there. Instead, use that space to update/insert the columns you want, and parameterize the input values:
-- Please don't name it 'table'
INSERT INTO my_table (column1, column2)
VALUES (?, ?)
ON DUPLICATE KEY UPDATE column1 = VALUES(column1), column2 = VALUES(column2)

How to update multiple columns using array of datas with a prepared statement?

I have 2 arrays :
columns = ['column1', 'column2'];
data = ['data1', 'data2'];
I'd like to update the table using a prepared query:
conn.query('UPDATE table SET ?? = ? WHERE id = ?', [columns, data, id],
function(err, info){
Excepted sql query :
UPDATE table SET column1 = 'data1', column2 = 'data2' WHERE id = 10
But I get something that looks like :
UPDATE table SET 'column1', 'column2' = 'data1', 'data2' WHERE id = 10
This feature works well for select or insert but it seems not working for update queries. Any thoughts on how I can get this work ?
From node-mysql docs, about escaping query values, we have this:
Arrays are turned into list, e.g. ['a', 'b'] turns into 'a', 'b'
, so it won't work the way you expect.
But, in the docs we also have this:
Objects are turned into key = 'val' pairs. Nested objects are cast to strings.
with an example:
var post = {id: 1, title: 'Hello MySQL'};
var query = connection.query('INSERT INTO posts SET ?', post, function(err, result) {
// Neat!
});
console.log(query.sql); // INSERT INTO posts SET `id` = 1, `title` = 'Hello MySQL'
So, in order to do what you want, the best option IMO, is to convert your arrays into an object like:
{
column1: 'data1',
column2: 'data2'
}
Just to clarify, since after Googling for ages I didn't find an exact example to show what I was looking for. Here is the code which hopefully is what Bobby Shark found out, since I don't think 'SET ??' works.
To UPDATE multiple columns (but not necessarily all) for one row, without typing up every column in the query, but passing an object of {column_name1: 'new_foo', column_name2: 'new_bar', column_name2: 'new_baz'}
(using body-parser to get object of form data)
var blog = req.body.blog; // object as described above
var sql = "UPDATE blog SET ? WHERE id = ?";
connection.query(sql, [blog, req.params.id], function(err, updatedBlog){
if(err){
console.log(err);
} else {
res.redirect("/blogs/" + req.params.id);
}
});
This post by Bala Clark helped (though we've read it in the docs 10 times!). Hope this helps to see example code of object with multiple column updates (name/value pairs). Again found no specific examples in many sites.

mysql insert new records using a loop

I need to set up some initial records in our mysql database.
I insert a new record which gives me the siteID of the last insert I then use this in creating the rows in my allNews_copy table. The following works (syntax is from applicationcraft ide) as I get the correct structure but there is a 60 timeout for the call to the server.
Therefore the script stops after about 270 rows.
for(i3=1; i3<51; i3++) {
//console.log('i3 = '+i3 );
for(i2=1; i2<101; i2++) {
//console.log('i2 = '+i2 );
var isVisible2 = 0;
if(i2 < 6){ isVisible2 = 1;}
cObj.insert('allNews_copy',{
siteID:siteID,
newsIDInt:i2,
catIDInt:i3,
title:'Item title '+i2 + ' in Cat' + i3,
newsDesc:'Item Desc',
visible:isVisible2
});
}
}
The total number of rows would be 5000.
So can I do this by using a mysql loop via a std mysql syntax?
In standard SQL syntax you can insert multiple rows in a single statement:
INSERT INTO table (<list of columns>)
VALUES (<list of values>), (<list of values>), (<list of values>), ...
I don't know how to translate this syntax to the API you're using, though.

How to get the last inserted id in a Linq To Sql custom sql expression?

Here is my problem.
I'd like to get the last inserted Id with a custom sql expression in Linq To Sql.
My insert method:
public int Add(string Label)
{
_dbContext.ExecuteCommand("INSERT INTO Products (Label) VALUES (#Label);", Label);
_dbContext.SubmitChanges();
var lastId = _dbContext.ExecuteQuery<int>("SELECT Scope_Identity() as [Scope_Identity];").ToList()[0];
return lastId;
}
lastId always returns null. When I tried this query (Insert + Select) directly in Sql Server, it works perfectly and returns the last inserted Id.
I don't want to use a procedure and I can't use a new Product object (it is not possible for me to use InsertOnSubmit or whatever).
Can you please help me ?
Thanks.
Ok I've found how to do it:
public int Add(string Label)
{
var query = String.Format("INSERT INTO Products (Label) VALUES (#Label); SELECT ProductId FROM Products WHERE ProductId = Scope_Identity();", Label);
var lastId = _dbContext.ExecuteQuery<int>(query).ToList()[0];
return lastId;
}
Try using:
INSERT INTO Products (Label) OUTPUT inserted.ProductID VALUES (#Label);
Rob
I know that you've already answered but there is anouther way of doing it which I found which was useful for my particular scenerio which involved needing to edit the latest records added to a table without knowing there ids:
public int Add(string Label)
{
var query = String.Format("INSERT INTO Products (Label) VALUES (#Label);", Label);
_dbContext.ExecuteCommand(query);
var lastId = _dbContext.ExecuteQuery<int>("SELECT IDENT_CURRENT('Products ')").First();
return lastId;
}