Persistent/static variables inside a MySQL trigger - mysql

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 !!!

Related

Cannot refer to tables with computed virtual columns in triggers of MySql

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)

Can't create table throught a view with function inside mysql

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.

how to create procedures in mysql

I am creating procedure in mysql. but I am facing some issues while creating that.
I am applying query i.e
CREATE PROCEDURE simpleproc( param1 INT) BEGIN SELECT COUNT(*)
INTO param1 FROM 91_nidhi; END//
and The error is
#1064 - You have an error in your SQL syntax; c
heck the manual that corresponds to your MySQL server version for the
right syntax to use near '' at line 1
This how you need to do
- You are not passing any input value rather you are using an output value
- so specify the param as OUT
Below is the example
mysql> create table 91_nidhi (id int,val varchar(20));
Query OK, 0 rows affected (0.09 sec)
mysql> insert into 91_nidhi values (1,'aa'),(2,'bb'),(3,'cc');
Query OK, 3 rows affected (0.00 sec)
Records: 3 Duplicates: 0 Warnings: 0
Now lets create the procedure
delimiter //
CREATE PROCEDURE simpleproc( out param1 INT)
BEGIN
SELECT COUNT(*) INTO param1 FROM 91_nidhi;
END; //
Then change the delimiter in the terminal as
mysql> delimiter ;
Now lets call the procedure
mysql> call simpleproc(#res);
Query OK, 0 rows affected (0.00 sec)
mysql> select #res ;
+------+
| #res |
+------+
| 3 |
+------+
1 row in set (0.00 sec)

Why doesn't this IF NOT EXISTS statement work?

I'm trying to write a query which will INSERT a random value between 0 to 9999 INTO a table, whereas this random value is yet to exist there.
However, nothing I wrote works. It seems like a WHERE clause doesn't work with INSERT, and my SQL server fails to execute an IF NOT EXISTS query. Is it incorrect, I wonder?
What should I do? Is there a solution to my problem?
(I'm using MySQL)
SET #rand = ROUND(RAND() * 9999);
IF NOT EXISTS (SELECT `num` FROM `nums` WHERE `num` = #rand)
INSERT INTO `nums` (`num`) VALUES (#rand);
You can do it like here: MySQL: Insert record if not exists in table
INSERT INTO `nums` (`num`)
SELECT *
FROM
(SELECT #rand) AS q
WHERE NOT EXISTS
(SELECT `num`
FROM `nums`
WHERE `num` = #rand);
Try it using a stored procedure like this:
CREATE PROCEDURE my_sp()
BEGIN
SET #rand = ROUND(RAND() * 9999);
IF NOT EXISTS (SELECT `num` FROM `nums` WHERE `num` = #rand) THEN
INSERT INTO `nums` (`num`) VALUES (#rand);
END IF;
END
Using statements like IF belongs inside a block of code like a stored procedure. You won't be able to execute it just on the mysql prompt.
If you just want to insert the a random value that wasn't there before you can also do it by
mysql> create table nums(num int, unique key(num));
Query OK, 0 rows affected (0.05 sec)
mysql> insert ignore into nums >select round(rand()*9999);>
Query OK, 1 row affected (0.01 >sec)>
Records: 1 Duplicates: 0 Warn>ings>: 0>
mysql> insert ignore into nums select round(rand()*9999);
Query OK, 1 row affected (0.00 sec)
Records: 1 Duplicates: 0 Warnings: 0
mysql> insert ignore into nums select round(rand()*9999);
Query OK, 1 row affected (0.00 sec)
Records: 1 Duplicates: 0 Warnings: 0
mysql> insert ignore into nums select round(rand()*9999);
Query OK, 1 row affected (0.00 sec)
Records: 1 Duplicates: 0 Warnings: 0
mysql> select * from nums;
+------+
| num |
+------+
| 5268 |
| 9075 |
| 9114 |
| 9768 |
+------+
4 rows in set (0.00 sec)
mysql>
With insert ignore, it won't insert a row if it already exists.

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