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()
Related
This seems to be a bug in MySql. Posting it here to confirm my conclusion and share my experience. We are currently migrating from MS SQL Server to MySql Community Edition 5.7.12. There is a Dealers table which has a virtual computed column. It was being referred in the join of a query used inside a trigger. As a result of this, the MySql Server got re-started.
To make sure that there was no other cause to the event, we had created a dummy table without computed columns and referred to that table in the trigger. The trigger executed successfully. Then, we had created another dummy table with the computed column. We had just referred the table in the join without the reference to the computed column. When the trigger was fired, the server crashed inspite of the fact that only a actual column of the table was referred and there was no reference to the computed column. Thus, you cannot even refer a table with computed columns in the triggers.
What we have done temporarily is to convert the virtual columns into actual columns and modified the queries of select, insert and update on the table.
Is there a better alternative to solve this issue?
Can you post your test example?. I can't reproduce the problem on my test example.
mysql> SELECT VERSION();
+-----------+
| VERSION() |
+-----------+
| 5.7.12 |
+-----------+
1 row in set (0.00 sec)
mysql> DROP TABLE IF EXISTS `t2`;
Query OK, 0 rows affected (0.00 sec)
mysql> DROP TABLE IF EXISTS `t1`;
Query OK, 0 rows affected (0.00 sec)
-- Table with Generated Column
mysql> CREATE TABLE IF NOT EXISTS `t1` (
-> `c0` INTEGER UNSIGNED NOT NULL PRIMARY KEY,
-> `value` VARCHAR(20),
-> `c1` INTEGER UNSIGNED GENERATED ALWAYS AS (`c0`) VIRTUAL
-> );
Query OK, 0 rows affected (0.00 sec)
mysql> CREATE TABLE IF NOT EXISTS `t2` (
-> `c1` INTEGER UNSIGNED NOT NULL PRIMARY KEY,
-> `value` VARCHAR(20)
-> );
Query OK, 0 rows affected (0.00 sec)
mysql> INSERT INTO `t2` (`c1`, `value`) VALUES (1, 'value 1');
Query OK, 1 row affected (0.00 sec)
mysql> DELIMITER ||
mysql> DROP TRIGGER IF EXISTS `t1_ins_bef`||
Query OK, 0 rows affected, 1 warning (0.00 sec)
mysql> CREATE TRIGGER `t1_ins_bef` BEFORE INSERT ON `t1`
-> FOR EACH ROW
-> BEGIN
-> SET NEW.`value` := (SELECT `t2`.`value`
-> FROM `t1`
-> INNER JOIN `t2` ON `t1`.`c1` = `t2`.`c1`);
-> END||
Query OK, 0 rows affected (0.00 sec)
mysql> DELIMITER ;
mysql> INSERT INTO `t1` (`c0`) VALUES (1), (2);
Query OK, 2 rows affected (0.00 sec)
Records: 2 Duplicates: 0 Warnings: 0
mysql> SELECT
-> `c0`,
-> `value`,
-> `c1`
-> FROM
-> `t1`;
+----+---------+------+
| c0 | value | c1 |
+----+---------+------+
| 1 | NULL | 1 |
| 2 | value 1 | 2 |
+----+---------+------+
2 rows in set (0.00 sec)
mysql> SELECT
-> `c1`,
-> `value`
-> FROM
-> `t2`;
+----+---------+
| c1 | value |
+----+---------+
| 1 | value 1 |
+----+---------+
1 row in set (0.00 sec)
I created two tables
CREATE TABLE `prova` (
`id` int NOT NULL AUTO_INCREMENT ,
`text` varchar(255) NOT NULL ,
PRIMARY KEY (`id`)
)
;
CREATE TABLE `prova2` (
`id2` int NOT NULL AUTO_INCREMENT ,
`text2` varchar(255) NOT NULL ,
PRIMARY KEY (`id2`)
)
;
insert into prova (text) values ('ffffff');
A function does a select on table one and inserts a row in table two only if the value of variable #test is set to 0:
CREATE FUNCTION `get_prova`()
RETURNS int(11)
BEGIN
declare id_prova int ;
declare test int ;
set #test = 1;
set #id_prova = (select id from prova limit 1);
if (#test = 0) THEN
insert into prova2 (text2) values ('dddd');
end if;
return #id_prova;
END;
then, I create a view that calls this function:
create view temp_prova as
select id,
text,
get_prova() as prova
from prova
I want to create table 3 that contains the result of view:
CREATE TABLE zzz_prova SELECT * FROM temp_prova;
but when I try to create table zzz_prova I get this error:
[SQL]CREATE TABLE zzz_prova SELECT * FROM temp_prova; [Err] 1746 -
Can't update table 'prova2' while 'zzz_prova' is being created.
Why does this error show up?
Thank you
What version of MySQL are you running?
Changes in MySQL 5.6.2 (2011-04-11)
Incompatible Change; Replication: It is no longer possible to issue a
CREATE TABLE ... SELECT statement which changes any tables other than
the table being created. Any such statement is not executed and
instead fails with an error.
One consequence of this change is that FOR UPDATE may no longer be
used at all with the SELECT portion of a CREATE TABLE ... SELECT.
This means that, prior to upgrading from a previous release, you
should rewrite any CREATE TABLE ... SELECT statements that cause
changes in other tables so that the statements no longer do so.
This change also has implications for statement-based replication
between a MySQL 5.6 (or later slave) and a master running a previous
version of MySQL. In such a case, if a CREATE TABLE ... SELECT
statement on the master that causes changes in other tables succeeds
on the master, the statement nonetheless fails on the slave, causing
replication to stop. To keep this from happening, you should either
use row-based replication, or rewrite the offending statement before
running it on the master. (Bug #11749792, Bug #11745361, Bug #39804,
Bug #55876)
References: See also Bug #47899.
UPDATE
MySQL 5.5:
mysql> SELECT VERSION();
+-----------+
| VERSION() |
+-----------+
| 5.5.47 |
+-----------+
1 row in set (0.00 sec)
mysql> DROP FUNCTION IF EXISTS `f`;
Query OK, 0 rows affected (0.00 sec)
mysql> DROP TABLE IF EXISTS `t1`;
Query OK, 0 rows affected (0.00 sec)
mysql> DROP TABLE IF EXISTS `t2`;
Query OK, 0 rows affected (0.00 sec)
mysql> DELIMITER |
mysql> CREATE FUNCTION `f`()
-> RETURNS INT
-> BEGIN
-> INSERT INTO `t2` VALUES (1);
-> RETURN 1;
-> END|
Query OK, 0 rows affected (0.00 sec)
mysql> DELIMITER ;
mysql> CREATE TABLE `t2`(`c1` INT);
Query OK, 0 rows affected (0.00 sec)
mysql> CREATE TABLE `t1` SELECT `f`() `c1`;
Query OK, 1 row affected (0.00 sec)
Records: 1 Duplicates: 0 Warnings: 0
mysql> SELECT `c1` FROM `t1`;
+------+
| c1 |
+------+
| 1 |
+------+
1 row in set (0.00 sec)
mysql> SELECT `c1` FROM `t2`;
+------+
| c1 |
+------+
| 1 |
+------+
1 row in set (0.00 sec)
MySQL 5.6:
mysql> SELECT VERSION();
+-----------------+
| VERSION() |
+-----------------+
| 5.6.25 |
+-----------------+
1 row in set (0.00 sec)
mysql> DROP FUNCTION IF EXISTS `f`;
Query OK, 0 rows affected, 1 warning (0.00 sec)
mysql> DROP TABLE IF EXISTS `t1`;
Query OK, 0 rows affected, 1 warning (0.00 sec)
mysql> DROP TABLE IF EXISTS `t2`;
Query OK, 0 rows affected, 1 warning (0.00 sec)
mysql> DELIMITER |
mysql> CREATE FUNCTION `f`()
-> RETURNS INT
-> BEGIN
-> INSERT INTO `t2` VALUES (1);
-> RETURN 1;
-> END|
Query OK, 0 rows affected (0.00 sec)
mysql> DELIMITER ;
mysql> CREATE TABLE `t2`(`c1` INT);
Query OK, 0 rows affected (0.00 sec)
mysql> CREATE TABLE `t1` SELECT `f`() `c1`;
ERROR 1746 (HY000): Can't update table 't2' while 't1' is being created.
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)
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.
I have the following trigger on a blackhole table, that intercepts inserts and passes them on to other tables.
In order to speed things up I want to calculate an increasing value and passes that in my inserts.
DELIMITER $$
CREATE TRIGGER ai_blackhole_each AFTER INSERT ON `test.blackhole` FOR EACH ROW
BEGIN
DECLARE calculated_id INTEGER;
SET calculated_id = calc_id_for_previous_insert + 1;
INSERT INTO example VALUES(new.field1, new.field2, calculated_id, ..);
END$$
DELIMITER ;
Can I have a static variable inside a trigger that keeps its value between firings?
Or is there a trick to achieve something like that efficiently?
Place an intermittent value in a MEMORY table starting at 0
use test
DROP TABLE IF EXISTS test.blackholecounter;
DROP TABLE IF EXISTS test.blackhole;
DROP TABLE IF EXISTS test.example;
CREATE TABLE test.blackholecounter (calc_id INT NOT NULL DEFAULT 0) ENGINE=MEMORY;
INSERT INTO test.blackholecounter VALUES (0);
CREATE TABLE test.blackhole
(
field1 VARCHAR(20),
field2 VARCHAR(20)
) ENGINE=BLACKHOLE;
CREATE TABLE test.example (field1 VARCHAR(20),
field2 VARCHAR(20),
calc_id INT);
DELIMITER $$
CREATE TRIGGER ai_blackhole_each AFTER INSERT ON test.blackhole
FOR EACH ROW
BEGIN
DECLARE calculated_id INTEGER;
SELECT calc_id INTO calculated_id FROM test.blackholecounter;
UPDATE test.blackholecounter SET calc_id=calc_id+1;
INSERT INTO test.example VALUES(new.field1, new.field2, calculated_id);
END
$$
DELIMITER ;
SELECT * FROM test.blackholecounter;
SELECT * FROM test.example;
INSERT INTO test.blackhole (field1,field2) VALUES ('rolando','edwards'),('pamela','edwards');
SELECT * FROM test.blackholecounter;
SELECT * FROM test.example;
Here is what I got when I pasted this into MySQL
mysql> use test
Database changed
mysql> DROP TABLE IF EXISTS test.blackholecounter;
Query OK, 0 rows affected (0.00 sec)
mysql> DROP TABLE IF EXISTS test.blackhole;
Query OK, 0 rows affected (0.01 sec)
mysql> DROP TABLE IF EXISTS test.example;
Query OK, 0 rows affected (0.06 sec)
mysql> CREATE TABLE test.blackholecounter (calc_id INT NOT NULL DEFAULT 0) ENGINE=MEMORY;
Query OK, 0 rows affected (0.05 sec)
mysql> INSERT INTO test.blackholecounter VALUES (0);
Query OK, 1 row affected (0.00 sec)
mysql> CREATE TABLE test.blackhole(field1 VARCHAR(20),field2 VARCHAR(20)) ENGINE=BLACKHOLE;
Query OK, 0 rows affected (0.06 sec)
mysql> CREATE TABLE test.example (field1 VARCHAR(20),field2 VARCHAR(20),calc_id INT);
Query OK, 0 rows affected (0.09 sec)
mysql> DELIMITER $$
mysql> CREATE TRIGGER ai_blackhole_each AFTER INSERT ON test.blackhole
-> FOR EACH ROW
-> BEGIN
-> DECLARE calculated_id INTEGER;
-> SELECT calc_id INTO calculated_id FROM test.blackholecounter;
-> UPDATE test.blackholecounter SET calc_id=calc_id+1;
-> INSERT INTO test.example VALUES(new.field1, new.field2, calculated_id);
-> END
-> $$
Query OK, 0 rows affected (0.11 sec)
mysql> DELIMITER ;
mysql> SELECT * FROM test.blackholecounter;
+---------+
| calc_id |
+---------+
| 0 |
+---------+
1 row in set (0.00 sec)
mysql> SELECT * FROM test.example;
Empty set (0.02 sec)
mysql> INSERT INTO test.blackhole (field1,field2) VALUES ('rolando','edwards'),('pamela','edwards');
Query OK, 2 rows affected (0.07 sec)
Records: 2 Duplicates: 0 Warnings: 0
mysql> SELECT * FROM test.blackholecounter;
+---------+
| calc_id |
+---------+
| 2 |
+---------+
1 row in set (0.00 sec)
mysql> SELECT * FROM test.example;
+---------+---------+---------+
| field1 | field2 | calc_id |
+---------+---------+---------+
| rolando | edwards | 0 |
| pamela | edwards | 1 |
+---------+---------+---------+
2 rows in set (0.00 sec)
You can start the initial value in the test.blackholecounter table with some other number or change the order when the increment happens in the trigger.
Give it a Try !!!