Partially failed insertion to related data - mysql

What should i do when i want to insert related data to different tables and one the insertions fail. So only a portion of the important related data is inserted to one of the tables. Then i obviously don't want that data fracture to stay in the table because it is not useful by itself. What are the best ways and technique to implement this kind of behaviour?

One of the best things that you can do is set the auto commit to 0. From there you can nest it in a transaction. That way you can provide a conditional that if the table doesn't fully update, roll back and it is not saved to your disk.
START TRANSACTION;
SELECT #A:=SUM(salary) FROM table1 WHERE type=1;
UPDATE table2 SET summary=#A WHERE type=1;
COMMIT;
I got this from the MYSQL website: http://dev.mysql.com/doc/refman/5.0/en/commit.html

This is what transactions are for.
A transaction wraps a bunch of transactions. At the end a transaction is either committed (all the changes made hit the disk) or rolled back (none of them are)
You start a transaction with the BEGIN statement, commit it with COMMIT, and roll it back with ROLLBACK

Related

MariaDB. Use Transaction Rollback without locking tables

On a website, when a user posts a comment I do several queries, Inserts and Updates. (On MariaDB 10.1.29)
I use START TRANSACTION so if any query fails at any given point I can easily do a rollback and delete all changes.
Now I noticed that this locks the tables when I do an INSERT from an other INSERT, and I'm not talking while the query is running, that’s obvious, but until the transaction is not closed.
Then DELETE is only locked if they share a common index key (comments for the same page), but luckily UPDATE is no locked.
Can I do any Transaction that does not lock the table from new inserts (while the transaction is ongoing, not the actual query), or any other method that lets me conveniently "undo" any query done after some point?
PD:
I start Transaction with PHPs function mysqli_begin_transaction() without any of the flags, and then mysqli_commit().
I don't think that a simple INSERT would block other inserts for longer than the insert time. AUTO_INC locks are not held for the full transaction time.
But if two transactions try to UPDATE the same row like in the following statement (two replies to the same comment)
UPDATE comment SET replies=replies+1 WHERE com_id = ?
the second one will have to wait until the first one is committed. You need that lock to keep the count (replies) consistent.
I think all you can do is to keep the transaction time as short as possible. For example you can prepare all statements before you start the transaction. But that is a matter of milliseconds. If you transfer files and it can take 40 seconds, then you shouldn't do that while the database transaction is open. Transfer the files before you start the transaction and save them with a name that indicates that the operation is not complete. You can also save them in a different folder but on the same partition. Then when you run the transaction, you just need to rename the files, which should not take much time. From time to time you can clean-up and remove unrenamed files.
All write operations work in similar ways -- They lock the rows that they touch (or might touch) from the time the statement is executed until the transaction is closed via either COMMIT or ROLLBACK. SELECT...FOR UPDATE and SELECT...WITH SHARED LOCK also get involved.
When a write operation occurs, deadlock checking is done.
In some situations, there is "gap" locking. Did com_id happen to be the last id in the table?
Did you leave out any SELECTs that needed FOR UPDATE?

MySql: SELECT ... FOR UPDATE break before doing the UPDATE

I'm doing a SELECT ... FOR UPDATE to lock one record, then doing some calculations and then doing the actual UPDATE. I'm working on an InnoDB-database.
But the calculations might end up in a state where I do not want to execute the UPDATE. How do I cancel the lock in this situation?
InnoDB is made for this. You need to manage your transactions.
Before doing your SELECT ... FOR UPDATE, do START TRANSACTION.
Then do your SELECT ... FOR UPDATE.
Then do the rest of your database work.
Then do COMMIT.
If you decide you don't want to complete your update work, do ROLLBACK instead of COMMIT, and every change within your transaction is discarded. The database returns to the state right before START TRANSACTION.
Don't forget the COMMIT or ROLLBACK especially when debugging. If you do forget, your database may appear to lock up, because a future SELECT ... FOR UPDATE operation may wait for your COMMIT. (Don't ask how I know this. :-)

Working of Transactions in INNODB engine

Consider a transaction T1,
Start transaction;
Update emp set emp_id=1 where emp_id=3;
commit;
The engine i am using is INNODB engine.
Before commit operation of the above shown transaction, I had accessed the table again it is showing the previous committed values. If the Row Level locking is placed on the table, it might have shown the error (you cannot access while some transaction is in the middle). Is there any wrong in my understanding.? Can any one help me on this?
Anything that is done as a part of a transaction is available to the same transaction even before the transaction is committed. The changes are not available in other transactions.
To test this, you need to update in one transaction and then from another terminal start a new transaction and try to access. The second transaction will be able to read the data but if you try to update the update will block and wait for the first transaction to be committed.
If you want the second select to wait and return the updated data you should use select for update.

MySQL which is the method to rollback a transaction effective?

I saw in multiple answers here and on google that rollback a transaction implies only the rollback of the last command, and i read also that implies ALL commands. (neither both documented or referenced by)
That I need to do is create a store procedure that insert/update on table A, get the last ID of A, insert that ID into B, get the last id of B, insert it into C, etc, etc, etc.
I want to know which is the method to commit or rollback all commands in the transaction, in order to start the transaction and if something fails, get back everything as the original.
SQL code with IF error and last_id will be preciated, because also I saw a LOT of differents ways to get the last id and I don't know which is better.
By the way, all tables are InnoDB
Kind regards,
If you BEGIN a transaction then nothing will get applied until you COMMIT it. Dropping your connection or issuing a ROLLBACK is the same as never committing it.
This is, of course, presuming you have autocommit set on, which is usually the case.
You can roll-back individual commands if you wrap them as transactions as well.
More information is available in the documentation.
Keep in mind that MyISAM and other engines do not support transactions, where InnoDB does. Further, only INSERT, UPDATE, DELETE and REPLACE statements are able to be rolled back. Other things, like alterations to the schema, are not.
As documented under START TRANSACTION, COMMIT, and ROLLBACK Syntax:
These statements provide control over use of transactions:
[ deletia ]
ROLLBACK rolls back the current transaction, canceling its changes.

Real-world uses of MySQL savepoints in web services?

Does anyone have experience they can share using MySQL savepoints (directly or via an ORM), especially in a non-trivial web service? Where have you actually used them? Are they reliable enough (assuming you're willing to run a fairly recent version of MySQL) or too bleeding-edge or expensive?
Lastly, does anyone have experience with something like the following use case and did you use savepoints for it? Say the main point of some specific unit of work is to add a row to an Orders table (or whatever, doesn't have to be order-related, of course) and update an OrdersAuditInfo table, in the same transaction. It is essential that Orders be updated if at all possible, but OrdersAuditInfo table is not as essential (e.g., it's ok to just log an error to a file, but keep going with the overall transaction). At a low level it might look like this (warning, pseudo-SQL follows):
BEGIN;
INSERT INTO Orders(...) VALUES (...);
/* Do stuff outside of SQL here; if there are problems, do a
ROLLBACK and report an error (i.e., Order is invalid in this
case anyway). */
SAVEPOINT InsertAudit;
INSERT INTO OrdersAudit(...) VALUES(...);
/* If the INSERT fails, log an error to a log file somewhere and do: */
ROLLBACK TO SAVEPOINT InsertAudit;
/* Always want to commit the INSERT INTO Orders: */
COMMIT;
But even here perhaps there'd be a better (or at least more common) idiom? One could do the OrdersAuditInfo insert in a completely different transaction but it would be nice to be guaranteed that the OrdersAuditInfo table were not written to unless the final COMMIT actually worked.
I generally tend to avoid SAVEPOINTs, as it can make code quite hard to understand and verify.
In the case you posted, wrapping in a single transaction will depend on whether having OrdersAudit records exactly corresponding with Orders, is part of your business rules.
EDIT: Just re-read your question, and you do not have a requirement for guaranteed correspondence between OrdersAudit and Orders. So I wouldn't use any transaction for the insertion of the OrdersAudit records.
I believe you are using a savepoint to avoid failed INSERTs to rollback the whole transaction. But in that case, the best way to do so is to use INSERT IGNORE. If it fails you will get a warning instead of an error, and nothing will rollback.
Since you don't need a rollback, I suggest you don't use a transaction.
SAVEPOINTs are great if you may want to rollback some successful statements (but not all statements). For example:
START TRANSACTION;
Relatively slow query that you don't want to run often
INSERT INTO a
SAVEPOINT one;
SELECT id INTO #id FROM slot WHERE status = 'free' ORDER BY timestamp LIMIT 1;
INSERT INTO b (slot_id, ...) VALUES (#id, ...)
INSERT INTO c (slot_id, ...) VALUES (#id, ...)
INSERT INTO d (slot_id, ...) VALUES (#id, ...)
COMMIT;
If the SELECT returns nothing, you may run a global ROLLBACK. If any of the following INSERTs fails, you may ROLLBACK TO SAVEPOINT one and pick another id. Obviously, in a case like this, you want to implement a maximum number of attempts in your code.