Do you need to update the data when it doesn't change - mysql

I want to update a row in the table,before updating, do I need to check if there is any change in each column?

In MySQL, you do not need to check the value. MySQL does not update the record if there is no change. That means that MySQL does not incur the overhead of logging or writing the data. There is a slight cost of checking if any values changed, and I think it does run the triggers.
Other databases behave differently.
This is in an arcane location in the documentation, where mysql_affected_rows() is described:
For UPDATE statements, the affected-rows value by default is the
number of rows actually changed.
. . .
An integer greater than zero indicates the number of rows affected or
retrieved. Zero indicates that no records were updated for an UPDATE
statement, no rows matched the WHERE clause in the query or that no
query has yet been executed.

That would be totally up to you to check if anything is in there before hand. You can do updates based on a single row, multiple, or all of them.
An example where you update a specific row is like this:
UPDATE your_table
SET Username='TestName123'
WHERE UserID='12486';
where you would be changing the username where the userid is 12486
OR you can update all of the rows with data you want like
UPDATE Customers
SET Country='USA'
This would update every record to have the Country column be filled with USA.

Related

DELETE query results in 'Query Interrupted' MySQL Workbench?

I can successfully delete records manually by click-selecting & deleting row(s) but executing delete queries result in 'Query Interrupted'.
My deletion queries are in the form:
DELETE FROM table where column = value;
The select statement uses the same values:
SELECT * FROM table WHERE column = value;
and returns desired results.
What could be causing the delete statement to fail? Are there limits on the amount of records you can delete at once in workbench?
If you wish to delete the entire contents of a table you can use Truncate.
TRUNCATE [TABLE] tbl_name
Please see the docs: https://dev.mysql.com/doc/refman/5.7/en/truncate-table.html
Using the DELETE function is usually used for deleting single rows.
According to the documentation, in the Preferences >> SQL Editor >> Other, the Safe Updates setting is on by default.
Safe Updates (rejects UPDATEs and DELETEs with no restrictions)
Enabled by default. Prevents UPDATE and DELETE queries that lack a corresponding key in a WHERE clause, or lack a LIMIT clause, from executing. This option requires a MySQL server reconnection.
When selected, this preference makes it possible to catch UPDATE and DELETE statements with keys that are not used properly and that can probably accidentally change or delete a large number of rows.
I think what this says is that if the setting is on, then the column you are filtering by in the DELETE or UPDATE statement must be the primary key, it cannot be just any column.
If you change the setting to off, then you might need to restart MySQL Workbench for the change to take effect (at least under Linux).
There is a default thousand-row limit in MySQL-Workbench. The SELECT query will return results but DELETE will fail if the number of records to be deleted exceeds one thousand. One option is to limit the results in the query itself or you can adjust the settings as stated in the documentation.

race condition in mysql select sql

What I try to accomplish seems simple,
Db type: MyISAM
Table Structure: card_id, status
Query: select an unused card_id from a table, and set the row as "used".
Is it race condition that when two queries running at the same time, and before status is updated, the same card_id is fetched twice?
I did some search already. It seems Lock table is a solution, but it's overkill to me and need Lock Privilege.
Any Idea?
Thanks!
It really depends on what statements you are running.
For plain old UPDATE statements against a MyISAM table, MySQL will obtain a lock on the entire table, so there is no "race" condition between two sessions there. One session will wait until the lock is released, and then proceed with it's own update (or will wait for a specified period, and abort with a "timeout".)
BUT, if what you are asking about is two sessions both running a SELECT against a table, to retrieve an identifier for a row to be updated, and both sessions retrieving the same row identifier, and then both sessions attempting to update the same row, then yes, that's a definite possibility, and one which really does have to be considered.
If that condition is not addressed, then it's basically going to be a matter of "last update wins", the second session will (potentially) overwrite the changes made by a previous update.
If that's an untenable situation for your application, then that does need to be addressed, either with a different design, or with some mechanism that prevents the second update from overwriting the update applied by the first update.
One approach, as you mentioned, is to avoid this situation by first obtaining an exclusive lock on the table (using a LOCK TABLES statement), then running a SELECT to obtain an identifier, and then running an UPDATE to update the identified row, and then finally, releasing the lock (using an UNLOCK TABLES statement.)
That's a workable approach for some low volume, low concurrency applications. But it does have some significant drawbacks. Of primary concern is reduced concurrency, due to the exclusive locks obtained on a single resource, which has the potential to cause a performance bottleneck.
Another alternative is an strategy called "optimistic locking". (As opposed to the previously described approach, which could be described as "pessimistic locking".)
For an "optimistic locking" strategy, an additional "counter" column is added to the table. Whenever an update is applied to a row in the table, the counter for that row is incremented by one.
To make use of this "counter" column, when a query retrieves a row that will (or might) be updated later, that query also retrieves the value of the counter column.
When an UPDATE is attempted, the statement also compares the current value of the "counter" column in the row with the previously retrieved value of the counter column. (We just include a predicate (e.g. in the WHERE clause) of the UPDATE statement. For example,
UPDATE mytable
SET counter = counter + 1
, col = :some_new_value
WHERE id = :previously_fetched_row_identifier
AND counter = :previously_fetched_row_counter
If some other session has applied an update to the row we are attempting to update (sometime between the time our session retrieved the row and before our session is attempting to do the update), then the value of the "counter" column on that row will have been changed.
The predicate on our UPDATE statement checks for that, and if the "counter" has been changed, that will cause our update to NOT be applied. We can then detect this condition (i.e. the affected rows count will be a 0 rather than a 1) and our session can take some appropriate action. ("Hey! Some other session updated a row we were intending to update!")
There are some good write-ups on how to implement an "optimistic locking" strategy.
Some ORM frameworks (e.g. Hibernate, JPA) provide support for this type of locking strategy.
Unfortunately, MySQL does NOT provide support for a RETURNING clause in an UPDATE statement, such as:
UPDATE ...
SET status = 'used'
WHERE status = 'unused'
AND ROWNUM = 1
RETURNING card_id INTO ...
Other RDBMS (e.g. Oracle) do provide that kind of functionality. With that feature of the UPDATE statement available, we can simply run the UPDATE statement to both 1) locate a row with status = 'unused', 2) change the value of status = 'used', and 3) return the card_id (or whatever columns we want) of the row the we just updated.
That gets around the problem of having to run a SELECT and then running a separate UPDATE, with the potential of some other session updating the row between our SELECT and our UPDATE.
But the RETURNING clause is not supported in MySQL. And I've not found any reliable way of emulating this type functionality from within MySQL.
This may work for you
I'm not entirely sure why I previously abandoned this approach using user variables (I mentioned above that I had played around with this. I think maybe I needed something more general, which would update more than one row and return a set of of id values. Or, maybe there was something that wasn't guaranteed about the behavior of user variables. (Then again, I only reference user variables in carefully constructed SELECT statements; I don't use user variables in DML; it may be because I don't have a guarantee of their behavior.)
Since you are interested in exactly ONE row, this sequence of three statements may work for you:
SELECT #id := NULL ;
UPDATE mytable
SET card_id = (#id := card_id)
, status = 'used'
WHERE status = 'unused'
LIMIT 1 ;
SELECT ROW_COUNT(), #id AS updated_card_id ;
It's IMPORTANT that these three statements run in the SAME database session (i.e. keep a hold of the database session; don't let go of it and get a new one.)
First, we initialize a user variable (#id) to a value which we won't confuse with a real card_id value from the table. (A SET #id := NULL statement would work as well, without returning a result, like the SELECT statement does.)
Next, we run the UPDATE statement to 1) find one row where status = 'unused', 2) change the value of the status column to 'used', and 3) set the value of the #id user variable to the card_id value of the row we changed. (We'd want that card_id column to be integer type, not character, to avoid any possible character set translation issues.)
Next, we run a query get the number of rows changed by the previous UPDATE statement, using the ROW_COUNT() function (we are going to need to verify that this is 1 on the client side), and retrieve the value of the #id user variable, which will be the card_id value from the row that was changed.
After I post this questions, I thought of a solution which is exactly the same as the one you mentioned at the end. I used update statement, which is "update TABLE set status ='used' where status = 'unused' limit 1", which returns the primary Id of the TABLE, and then I can use this primary ID to get cart_id. Says there are two update statements occurs at the same time, as you said, "MySQL will obtain a lock on the entire table, so there is no "race" condition between two sessions there", so this should solve my issue. But I am not sure why you said, "MySQL does NOT provide support an style statement".

What is wrong with this update query? I get "no records changed"

UPDATE sb_reviews
SET clients_id=(SELECT clients_id
FROM sb_users
WHERE id=sb_reviews.users_id)
sb_reviews is a table with a users_id and and a newly created clients_id. I'm trying to grab the clients_id from the sb_users table and put it into the sb_reviews table where sb_users.id=sb_reviews.users_id
This query does not work though, I get no records changed.
There is nothing wrong with your query.
In mysql, if the update has no effect (ie the value in the column isn't changing), it reports as not an update.
If you have already run this, you'll get "no records changed" message every run thereafter (unless data in the second table changes, of course).

MySQL: UPDATE trigger. Obtain the value of a column used in UPDATE's where clause if it fail to match any row?

MySQL: In update trigger's body, can I obtain the value of a column that is specified in the where clause of the triggering query if the where clause does not match any rows at all?
I have to do the following, but NOT USING direct query such as ON DUPLICATE KEY UPDATE so on:
If I have:
UPDATE my_table SET idiotism_level=5 WHERE name='Pencho'
... and the where clause match NO ROWS, I'd want to automatically trigger an insertion of a row having name='Pencho' before the update, and then the UPDATE would presumably match, and work properly.
Is it possible ?
This could be make in a RULE in other database systems (PostgreSQL), that does not exists in MySQL. It's a Rule and not a trigger as you should analyse the query and not the result of the query.
But for MySQL you can make pre-query jobs by using MySQL-Proxy. You should be able to alter your update query and build an insert, By running some 'check row exists' extra query from the MySQL-Proxy (I'm not saying this is a nice solution, but if you have no way to make the code to act better you can fix it at this level).
No. An update trigger fires once for each row that gets updated, not once for each update command that's executed. There's no way to make the trigger fire if nothing is updated. You would need to handle this in your application by checking the number of updated rows returned by your query.
If name has a unique index on it you can use REPLACE
REPLACE INTO my_table (idiotism,name) VALUES ( 5,'Pencho');

Performing an UPDATE or INSERT depending whether a row exists or not in MySQL

In MySQL, I'm trying to find an efficient way to perform an UPDATE if a row already exists in a table, or an INSERT if the row doesn't exist.
I've found two possible ways so far:
The obvious one: open a transaction, SELECT to find if the row exists, INSERT if it doesn't exist or UPDATE if it exists, commit transaction
first INSERT IGNORE into the table (so no error is raised if the row already exists), then UPDATE
The second method avoids the transaction.
Which one do you think is more efficient, and are there better ways (for example using a trigger)?
INSERT ... ON DUPLICATE KEY UPDATE
You could also perform an UPDATE, check the number of rows affected, if it's less than 1, then it didn't find a matching row, so perfom the INSERT.
There is another way - REPLACE.
REPLACE INTO myTable (col1) VALUES (value1)
REPLACE works exactly like INSERT, except that if an old row in the table has the same value as a new row for a PRIMARY KEY or a UNIQUE index, the old row is deleted before the new row is inserted. See Section 12.2.5, “INSERT Syntax”.
In mysql there's a REPLACE statement that, I believe, does more or less what you want it to do.
REPLACE INTO would be a solution, it uses the UNIQUE INDEX for replacing or inserting something.
REPLACE INTO
yourTable
SET
column = value;
Please be aware that this works differently from what you might expect, the REPLACE is quite literally. It first checks if there is a UNIQUE INDEX collision which would prevent an INSERT, it removes (DELETE) all rows which collide and then INSERTs the row you've given it.
This, for example, leads to subtle problems like Triggers not firing (because they check for an update, which never occurs) or values reverted to the defaults (because you must specify all values).
If you're doing a lot of these, it might be worth writing them to a file, and then using 'LOAD DATA INFILE ... REPLACE ...'