Preventing 2 users from updating the same record simultaneously - mysql

I have a table tbl_orders. It has a quantity field quantity.
In some part of my application I need to decrement the quantity by 1.
I already know the id of the record (available from the client side), so I issue an update statement:
UPDATE tbl_orders
SET quantity=quantity-1
WHERE id= 6
The problem is that this query can accidentally be run multiple times concurrently.
For example, 2 customer service operators may update the same record simultaneously.
That means that the quantity will be decremented by 2 when it is supposed to be decremented once only.
I tried putting the update in a transaction, but that resulted in only delaying the second transaction until the first one was committed. Once it was committed the second update ran and decremented the record again.
How can I make sure that other queries fail if one is modifying a record?
UPDATE:
for an update to be valid the quantity on the client side was the same in the database. For example if a user sees a quantity 5 on his browser and wants to decrement it, the value in database must be the same.
UPDATE 2
I found a good explanation here for optimistic locking using Doctrine 2:

One approach I've used/seen in the past was having a timestamp column. When querying, ensure you have both the ID and the original timestamp at the start of editing the record. Then, when you are trying to update, send via
update YourTable
set counter = counter -1,
TheTimestampColumn = new timestamp value
where ID = yourID
and TheTimeStampColumn = timeStampEditStartedWith
This way, whoever gets to it first with the original starting timestamp would win. For the one following, you would have to track how many records were updated, and if that count equals zero, then you would notify the user that another person made a change to the record while you were viewing it. Do you want to reload the latest data? (or something like that).

Related

Data manipulation in MySQL using window functions

I've a table in my database with record of an agent. The record looks like
Here, Site ID is a place where the Agent 1001 visits at logs himself in at TimeIn and then logs out of that site at TimeOut and raises some tickets and its corresponding amount. Now, If the site is not changed then I want to aggregate these records as shown in the table given below:
I am using MySQL database to do this. Using window functions I can find out consecutive matching Site ID and then mark it as some flag_variable (let's say 1) in a new column.
Now, for these rows where flag=1, I want to update current row TimeOut = next row Timeout. For three or more consecutive record of same Site ID this approach will not work, then we should need a recursive approach I guess.
My question is how can we do this update in the current table and remove redundant records in the table using MySQL syntax.

tracking actual changes in rows when updating table

I have a table which i keep on updating, from the values of other source in my code. The value I update may or may not be same as the value already in the row.
I need some kind of algorithm may be via mysql (db) or otherwise (part of code) so that I later may be able to identify which rows have a changed value.
There is a date modified column which I change. But that will not be a true indicator as it will always be updated. I want a way by which I can determine whether some predefined columns have changed values,
One solution is this: I can do a select query, then compare and update a changed flag in the table. But that seems complex and not for me as I have a table with a lot of records
Another solution might be to save the md5 checksum of the values in a column and while updating compare the previous md5 and current md5 and so on.
I want to know the best solution.
There's a fairly simple way to handle this problem. Let's think of it as managing when a row's timestamp gets updated.
First of all, as I'm sure you know, your table needs a timestamp column with default settings for INSERT and UPDATE. That looks like this if the column is called ts.
ts TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP
Second, you can use an UPDATE query like this to change the values in a row.
UPDATE stock
SET val1 = 'newval1',
val2 = 'newval2',
changed_by = 'current_user_id'
WHERE id = 'id_to_change'
AND NOT (val1 == 'newval1' AND val2 == 'newval2')
The AND NOT clause on the WHERE will prevent the update from taking place unless 'newval1' or 'newval2' would actually provide new values. This works because no rows match the WHERE clause in the update.
When the update is prevented from taking place your automatically set ts column will not change. Neither will the changed_by column be set to the present user's user_id. So, you have the time and user of the most recent genuine change.
Also, many host language interfaces to MySQL have a method call to determine how many rows were affected by a recent UPDATE operation. With this technique, you'll get back zero rows when the row is not updated. That might be convenient for your user interface.
Also, this technique uses a single query, so it's safe if more than one user is trying to update the same row at the same time. There's no need to use a transaction to guarantee that.
(Note that tracking the changed_by user is optional, and will only work if your application can provide the current user's actual id.)
This is reasonably efficient as long as the database search for WHERE id = 'id_to_change' works quickly.
It does require reworking your application's UPDATE queries. But so would any other approach to this problem.

New records since last query in MySQL View

I am looking for a way to create a View that when queried will automatically only retrieve new records since the last query. My tables have a timestamp field for all entries, so for a simple example I can
SELECT * WHERE timestamp >= 'blah'
but I don't know how to determine what blah should be from the last query. So if the View was queried at 11:00 and then again at 12:00, the query at 12:00 should only return records added since 11:00. And so on... This all needs to be accomplished in the View, the end user should simply be able to query the View and get the results.
Is this possible?
There are two ways:
Store last access date time in database per user persistent session
table, if you have one. On next view call to database, use the
previous latest access time in the session to filter rows starting
from.
Store last access date time in user virtual session at client
environment. On every call to server, send last access date time as
well. So that server uses it to filter rows starting from.
I prefer to use second option that process won't write any data in database tables.
As there may be an unread record that slips through undetected (say it came less than a second since the last one accessed, so it has the same timestamp), set a column to auto increment (typically labelled id) and check for entries using it e.g. in PHP save the last accessed record in a $lastId variable, and use:
$sql="SELECT * WHERE `id` > '$lastId'";

order and change one column of database

I have one col in my database named position used for ordering. However, when some records is deleted, the sequence get messed up. I want to reorder this col when the table is changed(maybe use trigger).
position(old) -> position(new)
1 1
3 2
7 3
8 4
like this.
I think there will not exist equal number even in position(old), because I have already attach some function in PHP to reorder the column when updates occurs. However, when a record is deleted because of the deletion of its parent, function will not be called.
Thanks for help!
If you are using the column just for ordering, you do not need to update column on deletion, because the order will still be correct. And you will save some resources.
But if you really need to update by sequence, look at this answer:
updating columns with a sequence number mysql
I believe (as scrowler wrote) the better way in such case is to update rows from the application, after application deletes the parent record.
If you decide to update it in the application then...
If position = n is deleted, you logic should be set position = position - 1 where position > n
Please note that this will work only if you delete one record at a time from your application and before assuming that before the delete is triggered the data is already in sequence

Select only recent updated rows in MySQL db with a large data volume

I'm working with a InnoDB table that contains up to 30000 records. Rows are updated frequenty with stock_quantity value. What i need to do is to select only the most recent updated rows with a scheduled task and perform some actions thru a WebService.
Just trying to understand which is the best way for doing this without kill performance. I'm thinking at 3 different solution:
using a datetime column and update its value on each modify. Then select rows where date_col > NOW()-20 min. (20 min. is the frequency crontab is running)
using a boolean column and set the value to true each time the row is modified. Then select rows where boolean_col is true. If the task is executed set back the value of boolean_col to false.
using a second table to store recent updated columns. On each update of a row in table_1 copy the row to table_2. Then select all rows from table_2, perform actions and truncate table_2.
Anyway I'm pretty sure the right solution is not listed up there... so does anyone have some good advice? Thanks.
fist at all,
30,000 record is not that big ...
i prefer method 1 with some additional changes
set the datetime column default to on update current_timestamp
build an index of this column
method 2 will incurred redundant write, and read
method 3 is the worse, it almost double x double the write and read operations
I would personally use your option 2.
I would seriously look at a tigger to set the value to 1 if the row is edited. Of course excluding and update that only effect the boolean col.
I would then have the cron search the table when boolean = 1, return the list process the file and update the field back to 0 once complete.
This would be my approach, but like you said there might be a better way.
Another Idea: You might also want to look at replacing your cron with the tigger and preform the action your cron does on record update might work...