I'm working on a financial system and I've got a problem with the MySQL transactions.
The system is a simple stock exchange, where users can buy and sell virtual shares. To keep integrity in buy and sell process I use transactions.
The problem is that in some cases (I don't know what it depends on) some of transactions are rolled back (or not commited), but next queries are processed.
The process is following:
User wants to buy shares for 1000 USD
In orderbook there are 4 offers for 250 USD
START TRANSACTION
For every offer:
Script does a UPDATE query (moving USD from one user to another and shares in the opposite way). Then script INSERTs entries to history tables.
Users pay a fee (UPDATE balances).
Repeat 5 and 6 for the next offer.
COMMIT
Now the key part - in some cases changes from point 5 are not saved, but from the 6 they are (I see that fee was paid, but there is no transaction in the history).
I'm not using ROLLBACK during this transactions and the script is not breaking (because in this case fee wouldn't be paid).
Is there any possibility that transaction is rolling back without ROLLBACK query? Or can MySQL COMMIT only few newest queries instead of all?
Transactions or not, it's the responsibility of your client code to verity that all your INSERT or UPDATE queries complete successfully and then either issue a explicit ROLLBACK or close the connection withour COMMIT to issue an implicity ROLLBACK. If any of them fails but your code goes on, those queries will not take effect (because they failed) but the rest will do.
Here's a simplified example:
mysql> create table test (
-> id int(10) unsigned not null,
-> primary key (id)
-> );
Query OK, 0 rows affected (0.02 sec)
mysql> insert into test(id) values (1);
Query OK, 1 row affected (0.00 sec)
mysql> start transaction;
Query OK, 0 rows affected (0.00 sec)
mysql> insert into test(id) values (2);
Query OK, 1 row affected (0.00 sec)
mysql> insert into test(id) values (-3);
ERROR 1264 (22003): Out of range value for column 'id' at row 1
We should have rollbacked and aborted here, but we didn't.
mysql> insert into test(id) values (4);
Query OK, 1 row affected (0.00 sec)
mysql> commit;
Query OK, 0 rows affected (0.00 sec)
mysql> select * from test;
+----+
| id |
+----+
| 1 |
| 2 |
| 4 |
+----+
3 rows in set (0.00 sec)
4 rows expected, got 3.
Other than that, there're many circumstances where you can get an unwanted COMMIT but an unwanted ROLLBACK is something I'm not sure that can happen unless you terminate a session with pending changes.
It's long time since the question was asked, but the problem actually was that I didn't check for the SQL errors for every query.
Actually at some points, when I should rollback transaction, I didn't do it.
If your are looking for answer - check again if you testing ALL queries in your transaction for successful execution and don't trust that the framework you are using is doing it for you automatically (just check it again).
Related
I have table with three records. There is one filed as auto_increment. The ID's are 1, 2, 3...
When I run query
SELECT LAST_INSERT_ID() FROM myTable
The result is 5, even the last ID is 3. Why?
And what is better to use? LAST_INSERT_ID() or SELECT MAX(ID) FROM myTable?
The LAST_INSERT_ID() function only returns the most recent autoincremented id value for the most recent INSERT operation, to any table, on your MySQL connection.
If you haven't just done an INSERT it returns an unpredictable value. If you do several INSERT queries in a row it returns the id of the most recent one. The ids of the previous ones are lost.
If you use it within a MySQL transaction, the row you just inserted won't be visible to another connection until you commit the transaction. So, it may seem like there's no row matching the returned LAST_INSERT_ID() value if you're stepping through code to debug it.
You don't have to use it within a transaction, because it is a connection-specific value. If you have two connections (two MySQL client programs) inserting stuff, they each have their own distinct value of LAST_INSERT_ID() for the INSERT operations they are doing.
edit If you are trying to create a parent - child relationship, for example name and email addresses, you might try this kind of sequence of MySQL statements.
INSERT INTO user (name) VALUES ('Josef');
SET #userId := LAST_INSERT_ID();
INSERT INTO email (user_id, email) VALUES (#userId, 'josef#example.com');
INSERT INTO email (user_id, email) VALUES (#userId, 'josef#josefsdomain.me');
This uses LAST_INSERT_ID() to get the autoincremented ID from the user row after you insert it. It then makes a copy of that id in #userId, and uses it twice, to insert two rows in the child table. By using more INSERT INTO email requests, you could insert an arbitrary number of child rows for a single parent row.
Pro tip: SELECT MAX(id) FROM table is a bad, bad way to figure out the ID of the most recently inserted row. It's vulnerable to race conditions. So it will work fine until you start scaling up your application, then it will start returning the wrong values at random. That will ruin your weekends.
last_insert_id() has no relation to specific tables. In the same connection, all table share the same.
Below is a demo for it.
Demo:
mysql> create table t1(c1 int primary key auto_increment);
Query OK, 0 rows affected (0.11 sec)
mysql> create table t2(c1 int primary key auto_increment);
Query OK, 0 rows affected (0.06 sec)
mysql> insert into t1 values(null);
Query OK, 1 row affected (0.01 sec)
mysql> insert into t2 values(4);
Query OK, 1 row affected (0.00 sec)
mysql> insert into t2 values(null);
Query OK, 1 row affected (0.02 sec)
mysql> select last_insert_id() from t1;
+------------------+
| last_insert_id() |
+------------------+
| 5 |
+------------------+
1 row in set (0.00 sec)
I don't think this function does what you think it does. It returns the last id inserted on the current connection.
If you compare that to SELECT MAX(ID) this selects the highest ID irrespective of connection, be careful not to get them mixed up or you will get unexpected results.
As for why it is showing 5 its probably because its the last id to be inserted, I believe that this value will remain even if the record is removed, perhaps someone could confirm this.
Table level triggers is what can come to rescue here. e.g. before insert trigger.
maybe you should restart the database connection than reconnected again for fresh data
When I try to create a database which already exists,
CREATE DATABASE IF NOT EXISTS test;
Query OK, 1 row affected (0.00 sec)
CREATE DATABASE IF NOT EXISTS test;
Query OK, 1 row affected, 1 warning (0.00 sec)
Why does it show 1 row affected message second time , even though it is not creating a new database with the same name?
Although the CREATE DATABASE IF NOT EXISTS test; command won't directly modify rows in an exiting instance of the test database, it will affect the actual details stored internally in the mysql database, or possibly in one of the derived meta views, like the information_schema or performance_schema etc.
The reported Query OK, 1 row affected (0.00 sec) is referring to a row in one of these internal data constructs. When you reissue the CREATE DATABASE command, and it fails gracefully thanks to the IF NOT EXISTS clause, it is still likely to store meta-data internally, maybe an accumulating field that counts warnings or similar, or even just a 'last acted on' timestamp against this database's row. In any case the stored data in this record is changed, and is reflected as an 'affected' row.
I want to show the result of rows affected after update, insert, or delete in mysql. I have put
DELETE FROM A WHERE ID='1';
SELECT ROW_COUNT();
With the ROW_COUNT the last statement, but the result show me is 0.
If you want to know number of rows affected by delete query in PHPMYADMIN then by running your query it will show you the result see below screenshot :
As #Flash Thunder said PHPmyadmin does not allow multiple queries sent at once
If you want to see the affected rows then you can also write a script using PHP which will exceute you sql query and returns the number of affected rows
Just to be clear.
phpMyAdmin is written in PHP, and PHP does not allow multiple queries sent at once... if you are sending two queries separately, second query is on new connection, so it has no access to previous query information. That's why SELECT ROW_COUNT(); returns 0.
But by default phpMyAdmin returns affected rows count in information after query. It probably uses mysql(i)_affected_rows() function.
FOUND_ROWS() returns number of tables in database when there was no previous query.
mysql> use hunting;
Reading table information for completion of table and column names
You can turn off this feature to get a quicker startup with -A
Database changed
mysql> select found_rows();
+--------------+
| found_rows() |
+--------------+
| 24 |
+--------------+
1 row in set (0.00 sec)
mysql> show tables;
(...)
24 rows in set (0.00 sec)
I have a question about MySQL InnoDB. For example: I have thread A to start a transaction:
mysql> start transaction;
Query OK, 0 rows affected (0.00 sec)
mysql> update user set name = "Jim" where id = 1;
Query OK, 1 row affected (0.00 sec)
Rows matched: 1 Changed: 1 Warnings: 0
And then, I just leave the thread A as it is. I started another thread B to do this:
mysql> start transaction;
Query OK, 0 rows affected (0.00 sec)
mysql> update user set name = "Tom" where id = 1;
And I got the error:
ERROR 1205 (HY000): Lock wait timeout exceeded; try restarting transaction
It makes sense, because thread A put a 'X' lock on that row.
And then I used thread B to do this:
mysql> start transaction;
Query OK, 0 rows affected (0.01 sec)
mysql> select * from user where id = 1;
+----+------+
| id | name |
+----+------+
| 1 | wlq3 |
+----+------+
1 row in set (0.00 sec)
Here I'm confused. Like I just said thread A put a 'X' lock on that row. why thread B can read this row. In my opinion, reading data need a share lock, but exclusive lock is already on that data. Is there anyone can help me ,thanks!
By the way, the isolation level of thread A is repeatable read, and thread B is read committed.
Well wlq3 is the committed value in the database so transaction B reads this value. The update with Jim has not committed yet.A lock will not block a read it will just block a update this is due to the MVCC locking model which is implemented in InnoDB.
See this link for more information: InnoDB's row locking the same as MVCC Non-Blocking Reads?
I have table with an auto-increment field, but I need to transfer the table to another table on another database. Will the value of field 1 be 1, that of field 2 be 2, etc?
Also in case the database get corrupted and I need to restore the data, will the auto-increment effect in some way? will the value change? (eg if the first row, id (auto-inc) = 1, name = john, country = UK .... will the id field remain 1?) I am asking because if other table refer to this value, all data will get out of sync if this field change.
It sounds like you are trying to separately insert data into two separate databases in the same order, and using the auto-increment field to link the two rows. It seems you are basically asking, is it OK to rely on the auto-increment being the same in both databases if the data is inserted in the same order.
If so, the answer is no - you cannot rely on this behaviour. It is legitimate for the auto-increment to skip a value, for example see here.
But maybe you are asking, can an auto-increment value suddenly change to another value after it is written and committed? No - they will not change in the future (unless of course you change them explicitly).
Does that answer your question? If not, perhaps you can explain your question again.
Transferring the data wouldn't be a problem, if you completely specify the auto_increment values. MySQL allows you to insert anything you want into an auto_increment field, but only does the actual auto_increment if the value you're inserting is 0 or NULL. At least on my 5.0 copy of MySQL, it'll automatically adjust the auto_increment value to take into account what you've inserted:
mysql> create table test (x int auto_increment primary key);
Query OK, 0 rows affected (0.01 sec)
mysql> insert into test (x) values (10);
Query OK, 1 row affected (0.00 sec)
mysql> insert into test (x) values (null);
Query OK, 1 row affected (0.00 sec)
mysql> insert into test (x) values (0);
Query OK, 1 row affected (0.00 sec)
mysql> insert into test (x) values (5);
Query OK, 1 row affected (0.00 sec)
mysql> select * from test;
+----+
| x |
+----+
| 5 | <--inserted '5' (#4)
| 10 | <--inserted '10' (#1)
| 11 | <--inserted 'null' (#2)
| 12 | <--inserted '0' (#3)
+----+
3 rows in set (0.00 sec)
You can also adjust the table's next auto_increment value as follows:
mysql> alter table test auto_increment=500;
Query OK, 4 rows affected (0.04 sec)
Records: 4 Duplicates: 0 Warnings: 0
mysql> insert into test (x) values (null);
Query OK, 1 row affected (0.00 sec)
mysql> select last_insert_id();
+------------------+
| last_insert_id() |
+------------------+
| 500 |
+------------------+
1 row in set (0.01 sec)
SELECT INTO should keep the same ids on target table
http://dev.mysql.com/doc/refman/5.1/en/ansi-diff-select-into-table.html
Using MySQL backup will do this, if you create your own insert statements make sure that you include your id field and that will insert the value (its not like MSSQL where you have to set identity_insert), a thing to watch for is that if you generate a DDL it sometimes generates "incrorectly" for your identity column (i.e. it states that starting point is at your last identity value? you may not want this behaviour).