it's the first time for me with triggers on MySQL.
I've two different tables ('users' and 'prova') and I want to insert a new row in 'prova' every time there is a new row in the 'users' table.
CREATE TRIGGER inserisciemail
AFTER INSERT ON users FOR EACH ROW
BEGIN
INSERT INTO prova (provaemail)
VALUES (NEW.email);
END
The field 'provaemail' results empty and only the id field is filled (autoincremented).
What's wrong?
Vito
I'm moving our discussion here because SO suggests to avoid extended discussions in comments.
So, thank you for the SQLs but I'm afraid they didn't include the CREATE TABLE for "prova" and the INSERT statement running on "users" table.
Anyways, I created the "users" table and the trigger on my dev environment. Then I created my own version of "prova" table as below:
CREATE TABLE `prova` (
`provaemail` VARCHAR(40)
) ENGINE=InnoDB DEFAULT CHARSET=latin1;
Then ran the below insert statement:
INSERT INTO `users` (`id`, `email`) VALUES (1, 'guptaabhay#gmail.com');
And a new entry was inserted to "prova", here:
mysql> select * from prova;
+----------------------+
| provaemail |
+----------------------+
| guptaabhay#gmail.com |
+----------------------+
1 row in set (0.00 sec)
So the trigger worked!
It would be great if you could share the schema for "prova" and your INSERT INTO users query so that we can research further. I'm sure something's amiss.
EDIT 1
Thank you for the INSERTs. They ran fine and the "prova" table has now the following entries:
mysql> select * from prova;
+-----------------------+
| provaemail |
+-----------------------+
| genoveffa#dominio.it |
| peto#dominio.it |
| test#dominio.it |
| gianni#dominio.it |
| nuovissimo#dominio.it |
| new#dominio.it |
| vit#dominio.it |
+-----------------------+
8 rows in set (0.00 sec)
So nothing weird till now! Why don't you try these steps once at your end:
create "users" table
create trigger
create "prova" table (using the CREATE statement I've given above)
fire the inserts as given on http://pastie.org/3166828
do SELECT * FROM prova;
And let me know whatever you see?
Related
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 ;
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)
I have one table in mysql database:
CREATE TABLE IF NOT EXISTS `t` (
`q` varchar(257) NOT NULL
) ENGINE=MyISAM DEFAULT CHARSET=latin1;
I added two values to it - one is through mysql-console and other from phpmyadmin:
insert into t(q) values(aes_encrypt('from phpmyadmin', 123456));
insert into t(q) values(aes_encrypt('from mysql console', 123456));
And I tried to display it:
select aes_decrypt(q,123456) from t;
From mysql-console I got the following out put:
mysql> select aes_decrypt(q,123456) from t;
+-----------------------+
| aes_decrypt(q,123456) |
+-----------------------+
| from phpmyadmin |
| from mysql console |
+-----------------------+
2 rows in set (0.00 sec)
From phpadmin I got the following output:
why phpmyadmin don't show correct output?
aes_decrypt function produces binary data.
Try
select cast(aes_decrypt(q,123456) as char) from t LIMIT 0, 30;
on your phpMyAdmin.
You can try the following query
select aes_decrypt(unhex(q),123456) from t;
Is there any way of performing in bulk a query like INSERT OR UPDATE on the MySQL server?
INSERT IGNORE ...
won't work, because if the field already exists, it will simply ignore it and not insert anything.
REPLACE ...
won't work, because if the field already exists, it will first DELETE it and then INSERT it again, rather than updating it.
INSERT ... ON DUPLICATE KEY UPDATE
will work, but it can't be used in bulk.
So I'd like to know if there's any command like INSERT ... ON DUPLICATE KEY UPDATE that can be issued in bulk (more than one row at the same time).
You can insert/update multiple rows using INSERT ... ON DUPLICATE KEY UPDATE. The documentation has the following example:
INSERT INTO table (a,b,c) VALUES (1,2,3),(4,5,6)
ON DUPLICATE KEY UPDATE c=VALUES(a)+VALUES(b);
Or am I misunderstanding your question?
One possible way to do this is to create a temporary table, insert the data into that, and then do 1 query with a join to insert the records that don't exist followed by and update to the fields that do exist. The basics would be something like this.
CREATE TABLE MyTable_Temp LIKE MyTable
LOAD DATA INFILE..... INTO MyTable_Temp
UPDATE MyTable INNER JOIN
MyTable_Temp
ON MyTable.ID=MyTable_Temp.ID
SET MyTable.Col1=MyTable_Temp.Col1, MyTable.Col2=MyTable_Temp.Col2.....
INSERT INTO MyTable(ID,Col1,Col2,...)
SELECT ID,Col1,Col2,...
FROM MyTable_Temp
LEFT JOIN MyTable
ON MyTable_Temp.ID = MyTable.ID
WHERE myTable.ID IS NULL
DROP TABLE MyTable_Temp
The syntax may not be exact, but this should give you the basics. Also, I know it's not pretty, but it gets the job done.
Update
I swapped the order of the insert and update, because doing insert first causes all the inserted rows to be updated when the update is called. If you do update first, only the existing records are updated. This should mean a little less work for the server, although the results should be the same.
Although this question has been answered correctly already (that MySQL does support this via ON DUPLICATE UPDATE with the expected multiple value set syntax), I'd like to expand on this by providing a demonstration that anyone with MySQL can run:
CREATE SCHEMA IF NOT EXISTS `test`;
DROP TABLE IF EXISTS test.new_table;
CREATE TABLE test.new_table (`Key` int(11) NOT NULL AUTO_INCREMENT, PRIMARY KEY (`Key`)) ENGINE=InnoDB AUTO_INCREMENT=106 DEFAULT CHARSET=utf8;
SELECT * FROM test.new_table;
INSERT INTO test.new_table VALUES (1),(2),(3),(4),(5) ON DUPLICATE KEY UPDATE `Key`=`Key`+100;
SELECT * FROM test.new_table;
INSERT INTO test.new_table VALUES (1),(2),(3),(4),(5) ON DUPLICATE KEY UPDATE `Key`=`Key`+100;
SELECT * FROM test.new_table;
The output is as follows:
Empty set (0.00 sec)
Query OK, 5 rows affected (0.00 sec)
Records: 5 Duplicates: 0 Warnings: 0
+-----+
| Key |
+-----+
| 1 |
| 2 |
| 3 |
| 4 |
| 5 |
+-----+
5 rows in set (0.00 sec)
Query OK, 10 rows affected (0.00 sec)
Records: 5 Duplicates: 5 Warnings: 0
+-----+
| Key |
+-----+
| 101 |
| 102 |
| 103 |
| 104 |
| 105 |
+-----+
5 rows in set (0.00 sec)
Try adding an insert trigger that does a pre-flight check and cancels the insert on duplicate key (after updating the existing row).
Not sure it'll scale well for bulk inserts, let alone work for load data infile, but it's the best I can think of. :-)
If you were using Oracle or Microsoft SQL, you could use the MERGE. However, MySQL does not have a direct correlation to that statement. There is the single-row solution that you mentioned but, as you pointed out, it doesn't do bulk very well. Here is a blog post I found on the difference between Oracle and MySQL and how to do what Oracle does with MERGE in MySQL:
http://blog.mclaughlinsoftware.com/2009/05/25/mysql-merge-gone-awry/
It isn't a pretty solution and it probably isn't as full a solution as you would like, but I believe that is the best there is for a solution.
Is there a way to attach a piece of metadata to a MySQL database? I'm trying to write code to automatically update the database schema whenever a code upgrade requires it. This requires the storage of a single integer value -- the schema version. I could of course create a whole table for it, but that seems like overkill for just a simple number.
You can use table comments to store the version:
ALTER TABLE table1 COMMENT = '1.4';
You'll have to regex to get the comment from this:
SHOW CREATE TABLE table1;
/COMMENT='(.*)'/
To answer the question as titled, that is for metadata for the entire database and not individual tables, there are a couple of choices, depending on the privileges that you have.
The most direct route is to create a stored function, which requires the CREATE ROUTINE privilege. e.g.
mysql> CREATE FUNCTION `mydb`.DB_VERSION() RETURNS VARCHAR(15)
RETURN '1.2.7.2861';
Query OK, 0 rows affected (0.03 sec)
mysql> SELECT `mydb`.DB_VERSION();
+--------------+
| DB_VERSION() |
+--------------+
| 1.2.7.2861 |
+--------------+
1 row in set (0.06 sec)
If your privileges limit you to only creating tables, you can create a simple table and put the metadata as default values. There’s no need to store any data in the table.
mysql> CREATE TABLE `mydb`.`db_metadata` (
`version` varchar(15) not null default '1.2.7.2861');
Query OK, 0 rows affected (0.00 sec)
mysql> SHOW COLUMNS FROM `mydb`.`db_metadata`;
+---------+-------------+------+-----+------------+-------+
| Field | Type | Null | Key | Default | Extra |
+---------+-------------+------+-----+------------+-------+
| version | varchar(15) | NO | | 1.2.7.2861 | |
+---------+-------------+------+-----+------------+-------+
1 row in set (0.00 sec)