MySQL Trigger after insert - mysql

Are MySQL AFTER INSERT triggers always being executed directly after the INSERT statement, or is it possible that 2 inserts occur and after that 2 triggers occur?
I'm writing this trigger namely:
CREATE DEFINER=`p28004_bf4`#`localhost` TRIGGER `setId`
AFTER INSERT ON `playerkills`
FOR EACH ROW
BEGIN
INSERT INTO globals () VALUES();
UPDATE playerkills SET globalId = LAST_INSERT_ID() WHERE id = ROW.id;
END
And I'm worried about what will happen if the insert statements somehow get interleaved, the globalId must always be consistent, like a global unique identifier accross multiple tables.
Globals table:
id (Primary Key, Int, Auto Increment)
Playerkills table:
id (Primary Key, int, Auto increment)
globalId (Key, Int)
etc.

Ultimately it doesn't matter what order concurrent commands run in this case. The LAST_INSERT_ID function is smart enough not to give you the ID inserted by a someone else's concurrent query.
As a relatively simple example, I opened two mysql sessions and created a table called globals with an autoincrement primary key, then alternated back and forth typing these commands.
## Session 1 ## ## Session 2 ##
mysql> INSERT INTO globals () VALUES ();
Query OK, 1 row affected (0.00 sec)
mysql> INSERT INTO globals () VALUES ();
Query OK, 1 row affected (0.00 sec)
mysql> SELECT LAST_INSERT_ID();
+------------------+
| LAST_INSERT_ID() |
+------------------+
| 1 |
+------------------+
1 row in set (0.00 sec)
mysql> SELECT LAST_INSERT_ID();
+------------------+
| LAST_INSERT_ID() |
+------------------+
| 2 |
+------------------+
1 row in set (0.00 sec)

Related

ON DUPLICATE KEY UPDATE - decrement value in MySQL

The following seems odds to me:
INSERT INTO sometable (UNIQUEVALUE,NUMERICVALUE) VALUES ('valuethatexists','100') ON DUPLICATE KEY UPDATE NUMERICVALUE = NUMERICVALUE+VALUES(NUMERICVALUE);
Assume your NUMERICVALUE is at 0.
The above would change it to 100 - which does work.
If, however, you then input -100, it does not work properly.
INSERT INTO sometable (UNIQUEVALUE,NUMERICVALUE) VALUES ('valuethatexists','-100') ON DUPLICATE KEY UPDATE NUMERICVALUE = NUMERICVALUE+VALUES(NUMERICVALUE);
The above statement should return it to 0. It does not, in my case. It remains at 100.
Am I missing something?
Edit: This goes wrong somewhere else. I am doing this with PHP. The actual code exhibiting this bug looks like this:
Edit 2: This had nothing to do with PHP either. The problem was the NUMERIC value was UNSIGNED in my production environment, meaning VALUES(NUMERICVALUE) was brought from -100 to 0 before it was used.
On my MySQL server (5.7.12), it does work as expected:
mysql> CREATE TABLE sometable (
UNIQUEVALUE VARCHAR(16) NOT NULL PRIMARY KEY,
NUMERICVALUE INT NOT NULL);
Query OK, 0 rows affected (0.01 sec)
mysql> INSERT INTO sometable (UNIQUEVALUE,NUMERICVALUE)
VALUES ('valuethatexists','100')
ON DUPLICATE KEY UPDATE NUMERICVALUE = NUMERICVALUE+VALUES(NUMERICVALUE);
Query OK, 1 row affected (0.01 sec)
mysql> SELECT * FROM sometable;
+-----------------+--------------+
| UNIQUEVALUE | NUMERICVALUE |
+-----------------+--------------+
| valuethatexists | 100 |
+-----------------+--------------+
1 row in set (0.00 sec)
mysql> INSERT INTO sometable (UNIQUEVALUE,NUMERICVALUE)
VALUES ('valuethatexists','-100')
ON DUPLICATE KEY UPDATE NUMERICVALUE = NUMERICVALUE+VALUES(NUMERICVALUE);
Query OK, 2 rows affected (0.00 sec)
mysql> SELECT * FROM sometable;
+-----------------+--------------+
| UNIQUEVALUE | NUMERICVALUE |
+-----------------+--------------+
| valuethatexists | 0 |
+-----------------+--------------+
1 row in set (0.00 sec)
Which version of MySQL are you using? Can you execute the exact statements above and see if you have different results?
While Benjamin's answer is correct, the root of the issue turned out to be the fact that the NUMERICVALUE column was UNSIGNED, so whenever I input -100, it was turned into 0 before it was evaluated as VALUES(NUMERICVALUE). If this is to be considered a bug or not I don't know.
Obviously the result of the final evaluation should not be negative, but I don't know how clever it is to silently turn it into 0. I had logic in place making sure the value in question would never be below 0 anyway by never passing a negative value larger than what was already in the row.

MySQL after delete Trigger

I have 2 tables member_details and archives. I want a trigger that will insert deleted data from member_details into archives as soon as as a particular record is deleted from the member_details table.
DELIMITER $$
CREATE TRIGGER member_details_ADEL AFTER DELETE ON member_details
FOR EACH ROW
insert into archives values
This is how you can do it, lets say you have a table called test with the following data
mysql> select * from test ;
+----------------------+
| id |
+----------------------+
| 10 |
| 20 |
| 30 |
+----------------------+
You have another table called test1 as no data currently
mysql> select * from test1 ;
Empty set (0.00 sec)
Now lets write a trigger so that when there is a delete on the test table the deleted record gets added to the test1 table. You need to use after delete trigger for this and the using old.col you can get the data
In this example I have only one column you can use old.col name to access any column on the table where the trigger is executing
delimiter //
create trigger log_delete after delete on test
for each row
begin
insert into test1 (id) values (old.id);
end ; //
delimiter ;
Now lets delete a record in test table as
mysql> delete from test where id = 10 ;
Query OK, 1 row affected, (0.00 sec)
mysql> select * from test ;
+----------------------+
| id |
+----------------------+
| 20 |
| 30 |
+----------------------+
2 rows in set (0.00 sec)
mysql> select * from test1 ;
+----------------------+
| id |
+----------------------+
| 10 |
+----------------------+
1 row in set (0.00 sec)
Now as you can see the deleted row has been added to the table test1.
All you need to do is to set proper table names and the column names in the above trigger along with the trigger name you want to have
USE church;
DELIMITER $$
CREATE TRIGGER member_details_ADEL AFTER DELETE ON member_details
FOR EACH ROW
begin
insert into archives
(TITLE, NAME, `NAME OF SPOUSE`, `LEVEL OF EDUCATION`, ADDRESS, `PHONE NUMBER`, OCCUPATION, `DATE JOINED`, `EMERGENCY CONTACT`)
values
(old.TITLE, old.NAME, old.`NAME OF SPOUSE`, old.`LEVEL OF EDUCATION`, old.`ADDRESS`, old.`PHONE NUMBER`,old.`OCCUPATION`, old.`DATE JOINED`, old.`EMERGENCY CONTACT`);
END ; $$
delimiter ;
Make sure to backtick the column names when there is a space in between or in case using a reserve keyword.
You can use OLD keyword to fetch values from the deleted record.
But you have to specify column names with the insert statement.
Example :
delimiter //
drop trigger if exists member_details_adel //
create trigger member_details_adel
after delete on member_details
for each row
insert
into archives( col1, col2, colx )
values( old.col1, old.col2, old.colx );
//
delimiter ;
Change and extend column names as per your table needs.
Update 1:
There was syntax error in your tried trigger body.
Corrected one is as follows:
delimiter $$
create trigger member_details_adel
after delete on member_details
for each row
insert
into archives (title, name, name of spouse,
level of education, address,
phone number, occupation,
date joined, emergency contact)
values (old.title, old.name, old.`name of spouse`,
old.`level of education`, old.`address`,
old.`phone number`, old.`occupation`,
old.`date joined`, old.`emergency contact`);
$$
delimiter ;

auto increment second column [duplicate]

This question already has answers here:
Concatenating a string and primary key Id while inserting
(2 answers)
Closed 6 years ago.
I have a table with 2 columns. The ID column auto increments. I'm trying to auto increment the user column with the same ID as the id column, but with a "user" prefix (example: user100, where the ID is also 100) basically just like what is done on stackoverflow.
CREATE TABLE test_table (
id MEDIUMINT NOT NULL AUTO_INCREMENT,
user CHAR(30) NOT NULL,
PRIMARY KEY (id)
) ENGINE=MyISAM;
Is there a way of doing this in 1 query? Instead of inserting into the DB, then querying to get the ID, and inserting the ID into the user column?
Use a BEFORE trigger:
DELIMITER $$
CREATE TRIGGER test_table_trigger
BEFORE INSERT ON test_table
FOR EACH ROW BEGIN
SET NEW.`user` = CONCAT(NEW.`user`, NEW.id);
END $$
DELIMITER ;
Documentation: MySQL triggers
You can do a trigger
Before Trigger:
mysql> truncate table test_table;
Query OK, 0 rows affected (0.00 sec)
mysql> delimiter $$
mysql> CREATE TRIGGER test_table_trigger
-> BEFORE insert ON test_table
-> FOR EACH ROW
-> BEGIN
-> SET new.user = CONCAT('user', (SELECT AUTO_INCREMENT FROM information_schema.TABLES WHERE TABLE_SCHEMA=DATABASE() AND TABLE_NAME='test_table'));
-> END $$
Query OK, 0 rows affected (0.00 sec)
mysql> delimiter ;
mysql> INSERT INTO test_table values ();
Query OK, 1 row affected, 1 warning (0.00 sec)
mysql> INSERT INTO test_table values ();
Query OK, 1 row affected, 1 warning (0.00 sec)
mysql> INSERT INTO test_table values ();
Query OK, 1 row affected, 1 warning (0.00 sec)
mysql> select * FROM test_table;
+----+-------+
| id | user |
+----+-------+
| 1 | user1 |
| 2 | user2 |
| 3 | user3 |
+----+-------+
3 rows in set (0.00 sec)
The above should then use the auto-increment after it's designated to the id column and append it to the string user. The auto increment ID is pulled from Information_Schema, as if this is in a transaction or many queries, it could be set wrong.
Maybe you can try this, picking up last inserted id and concatenating string with converted value:
INSERT INTO test_table (user) VALUES ('user')
UPDATE test_table
SET user = user + CAST(LAST_INSERT_ID() AS VARCHAR)
WHERE id = LAST_INSERT_ID()

How do I detect if the ON UPDATE event fired with query in MYSQL?

Say I have a query with an ON DUPLICATE stanza:
INSERT INTO table (a) VALUES (0)
ON DUPLICATE KEY UPDATE a=1
How do I find out, after I've run this query, whether the query performed an insert, or an update?
The ROW_COUNT function can discriminate, returning 1 or 2 if the INSERT was "pure" or collided.
Per the docs:
For INSERT ... ON DUPLICATE KEY UPDATE statements, the affected-rows value is 1 if the row is inserted as a new row and 2 if an existing row is updated.
Example:
mysql> create table t (a int not null unique) engine=innodb;
Query OK, 0 rows affected (0.05 sec)
mysql> insert into t (a) values (0) on duplicate key update a=1;
Query OK, 1 row affected (0.00 sec)
mysql> select row_count();
+-------------+
| row_count() |
+-------------+
| 1 |
+-------------+
1 row in set (0.00 sec)
mysql> insert into t (a) values (0) on duplicate key update a=1;
Query OK, 2 rows affected (0.00 sec)
mysql> select row_count();
+-------------+
| row_count() |
+-------------+
| 2 |
+-------------+
1 row in set (0.00 sec)
You can use an extra column for the if, and name it as flag (int or tinyint) and set its default to 0, and change query like:
INSERT INTO table (a, flag) VALUES (0, 1) ON DUPLICATE KEY UPDATE a=1;
Now if insert is successful, the flag is set to 1 otherwise it's 0.
On a professional level, this flag example is very useful in many ways.

MySQL auto increment

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).