I would like to auto_increment two different tables in a single mysql database, the first by multiples of 1 and the other by 5 is this possible using the auto_increment feature as I seem to only be able to set auto_increment_increment globally.
If auto_increment_increment is not an option what is the best way to replicate this
Updated version: only a single id field is used. This is very probably not atomic, so use inside a transaction if you need concurrency:
http://sqlfiddle.com/#!2/a4ed8/1
CREATE TABLE IF NOT EXISTS person (
id INT NOT NULL AUTO_INCREMENT,
PRIMARY KEY ( id )
) ENGINE=InnoDB DEFAULT CHARSET=utf8 AUTO_INCREMENT=1;
CREATE TRIGGER insert_kangaroo_id BEFORE INSERT ON person FOR EACH ROW BEGIN
DECLARE newid INT;
SET newid = (SELECT AUTO_INCREMENT
FROM information_schema.TABLES
WHERE TABLE_SCHEMA = DATABASE()
AND TABLE_NAME = 'person'
);
IF NEW.id AND NEW.id >= newid THEN
SET newid = NEW.id;
END IF;
SET NEW.id = 5 * CEILING( newid / 5 );
END;
Old, non working "solution" (the before insert trigger can't see the current auto increment value):
http://sqlfiddle.com/#!2/f4f9a/1
CREATE TABLE IF NOT EXISTS person (
secretid INT NOT NULL AUTO_INCREMENT,
id INT NOT NULL,
PRIMARY KEY ( secretid )
) ENGINE=InnoDB DEFAULT CHARSET=utf8 AUTO_INCREMENT=1;
CREATE TRIGGER update_kangaroo_id BEFORE UPDATE ON person FOR EACH ROW BEGIN
SET NEW.id = NEW.secretid * 5;
END;
CREATE TRIGGER insert_kangaroo_id BEFORE INSERT ON person FOR EACH ROW BEGIN
SET NEW.id = NEW.secretid * 5; -- NEW.secretid is empty = unusuable!
END;
Related
Challenge:
Create a method to set "auto_increment" values for tables in a non-sequential way.
The goal is to override the "auto_increment" mechanism and allow the function "LAST_INSERT_ID()" to continue working as expected (returning an INT), so that no changes are needed in software side.
My Solution
The method I found is based on an auxiliary table (unique_id), that stores values available to be assigned. Values are then selected randomly, and removed from the tables as used. When the table gets empty, a new set of ID's is created.
This example is working as expected, but with one problem.
Tables for the demo:
CREATE TABLE `unique_id` (
`id` INT(10) UNSIGNED NOT NULL AUTO_INCREMENT,
PRIMARY KEY (`id`)
)
COLLATE='latin1_swedish_ci'
ENGINE=InnoDB
AUTO_INCREMENT=100;
CREATE TABLE `test_unique_id` (
`id` INT(10) UNSIGNED NOT NULL AUTO_INCREMENT,
`name` VARCHAR(50) NOT NULL DEFAULT '0',
PRIMARY KEY (`id`)
)
COLLATE='latin1_swedish_ci'
ENGINE=InnoDB
AUTO_INCREMENT=1;
Defined a stored procedure and a function:
DELIMITER $$
DROP PROCEDURE IF EXISTS `UNIQUE_ID_REFILL`$$
CREATE PROCEDURE UNIQUE_ID_REFILL()
BEGIN
DECLARE a INT Default 0 ;
simple_loop: LOOP
SET a=a+1;
INSERT INTO unique_id (id) values(null);
IF a=100 THEN
LEAVE simple_loop;
END IF;
END LOOP simple_loop;
END $$
DROP FUNCTION IF EXISTS `UNIQUE_ID_GET`$$
CREATE FUNCTION UNIQUE_ID_GET()
RETURNS INT(11)
MODIFIES SQL DATA
BEGIN
DECLARE new_id INT(11);
DECLARE unique_id_count INT(11);
SET new_id = 0;
SELECT COUNT(*) INTO unique_id_count FROM unique_id;
IF unique_id_count=0 THEN
CALL UNIQUE_ID_REFILL();
END IF;
SELECT id INTO new_id FROM unique_id ORDER BY RAND() LIMIT 1;
DELETE FROM unique_id WHERE id = new_id;
RETURN new_id;
END $$
Created a Trigger on the destination table (test_unique_id):
CREATE TRIGGER test_unique_id__unique_id BEFORE INSERT ON test_unique_id
FOR EACH ROW
SET NEW.id = UNIQUE_ID_GET();
The solution is getting the random ID's as expected:
INSERT INTO test_unique_id(name) VALUES ('A'),('B'),('C');
Creates the rows:
id name
154 'A'
129 'B'
173 'C'
The Problem
The main problem is that LAST_INSERT_ID() stops working... and the software side is broken:
SELECT LAST_INSERT_ID();
0
Any ideas on how to solve this problem? or any other different approach to the challenge?
Thank you very much.
I'm having difficulty with developing the logic in MySQL. I don't know how to INSERT multiple records from a Table AFTER UPDATE.
CREATE TABLE primeira(
ID int PRIMARY KEY AUTO_INCREMENT,
nome varchar(30) NOT NULL,
valor int DEFAULT 0
);
CREATE TABLE segunda(
ID int PRIMARY KEY AUTO_INCREMENT,
ID_primeira int,
ultimo_valor int DEFAULT 0,
credito int NOT NULL,
limite int DEFAULT 0,
FOREIGN KEY(ID_primeira) references primeira(ID)
);
CREATE TABLE terceira(
ID int PRIMARY KEY AUTO_INCREMENT,
ID_segunda int,
`data` TIMESTAMP DEFAULT CURRENT_TIMESTAMP NOT NULL,
estado boolean DEFAULT false,
FOREIGN KEY(ID_segunda) references segunda(ID)
);
CREATE TRIGGER tr_segundaLimite_INS
BEFORE INSERT ON segunda FOR EACH ROW
SET NEW.limite = New.ultimo_valor + New.credito;
DELIMITER //
CREATE TRIGGER tr_primeira_UPD
AFTER UPDATE ON primeira FOR EACH ROW
IF (SELECT limite FROM segunda WHERE segunda.ID_primeira = New.ID AND
(limite - NEW.valor)< 50) THEN
INSERT INTO terceira(ID_segunda)
VALUES ((SELECT ID FROM segunda WHERE segunda.ID_primeira = New.ID
AND (limite - NEW.valor)< 50));
END IF;
END
//
DELIMITER ;
I'm going to use procedures with functions to SELECT the data. The problem with this TRIGGER is that it's not working when there are multiple matching records.
The error that I am getting is-
subquery returns more than 1 row.
The objective is: After an update of the primeira.valor, the trigger would subtract segunda.limite - New.valor. If this difference is < 50 then all the matching segunda.ID would be registered at terceira.ID_segunda on terceira table.
I'm using data below:
INSERT INTO primeira(nome,valor)
VALUES
('Burro',800),
('Chiconizio',300),
('Xerosque',400),
('Shrek',600);
INSERT INTO segunda(ID_primeira,ultimo_valor,credito)
VALUES
(1,600,800),
(1,700,400),
(1,800,500),
(2,150,200),
(2,200,180),
(2,250,300);
UPDATE primeira
SET Valor = 330
WHERE ID = 2;
You need CURSOR for this. You can try the following trigger code. I hope this will fix your issue.
DELIMITER //
CREATE TRIGGER tr_primeira_UPD
AFTER UPDATE ON primeira FOR EACH ROW
BEGIN
DECLARE v_limite_diff INT;
DECLARE v_seg_id INT;
DECLARE done INT DEFAULT FALSE;
DECLARE c1 CURSOR FOR SELECT (s.limite - NEW.valor), s.id
FROM segunda s WHERE s.ID_primeira = New.ID;
DECLARE CONTINUE HANDLER FOR NOT FOUND SET done = TRUE;
OPEN c1;
my_loop: LOOP
FETCH c1 INTO v_limite_diff, v_seg_id;
IF done THEN
LEAVE my_loop;
END IF;
IF( v_limite_diff < 50 ) THEN
INSERT INTO terceira(ID_segunda) VALUES(v_seg_id);
END IF;
END LOOP;
END//
DELIMITER ;
I've currently made a simple record table, with the recordID as the auto-increment primary key, the question is, due to religious reasons, my employer DOES NOT want to include the number 4 and 6 in the recordID, so instead of checking the recordID everytime after the record has been made, is there a much easier way to solve my current problem?
EDIT:
Here's a quick test table I've created based on Vanojx1's answer. So what did I do wrong?
CREATE TABLE `test` (
`ID` int(11) NOT NULL,
`value` int(11) NOT NULL
)
DELIMITER $$
CREATE TRIGGER `jump4and6` BEFORE INSERT ON `test` FOR EACH ROW BEGIN
SET #nextId = (SELECT MAX(`id`) FROM `test`);
IF (#nextId IN (4,6)) THEN
SET NEW.id = #nextId + 1;
SET #nextId = #nextId + 2;
ELSE
SET NEW.id = #nextId;
SET #nextId = #nextId + 1;
END IF;
INSERT INTO `test`(`id`) VALUES (#nextId);
END
$$
DELIMITER ;
ALTER TABLE `test` ADD PRIMARY KEY (`ID`);
ALTER TABLE `test` MODIFY `ID` int(11) NOT NULL AUTO_INCREMENT;
Everything works so far, but when I tried to insert a row:
INSERT INTO `test`(value) VALUES (123456);
This happens.
#1442 - Can't update table 'test' in stored function/trigger because it is already used by statement which invoked this stored function/trigger.
You can create the primary key as an integer, then use a trigger before insert like this:
DELIMITER $$
CREATE TRIGGER jump4and6
BEFORE INSERT
ON your_table
FOR EACH ROW BEGIN
SET #nextId = (SELECT MAX(current_index) FROM your_table_sequence);
IF (#nextId IN (4,6)) THEN
SET NEW.id = #nextId + 1;
SET #nextId = #nextId + 2;
ELSE
SET NEW.id = #nextId;
SET #nextId = #nextId + 1;
END IF;
INSERT INTO your_table_sequence (current_index) VALUES (#nextId);
END$$
You also need a table to store your primary key sequence
I am trying to create to trigger to divide my primary key into two groups.
Here is my table:
CREATE TABLE `q_locations` (
`id` int(11) NOT NULL,
`name` varchar(300) NOT NULL,
`standalone` bit(1) NOT NULL DEFAULT b'0',
UNIQUE KEY `id` (`id`),
KEY `standalone` (`standalone`)
)
If standalone is 0, id should start from 1, if standalone = 1, id should start from 1000. Id should be increment after each insert.
My trigger:
DELIMITER $$
CREATE TRIGGER trigger_insert_q_locations
BEFORE INSERT ON q_locations
FOR EACH ROW
BEGIN
SET New.id = (
SELECT coalesce(max(id) + 1, (case when standalone = 0 then 1 else 1000 end))
FROM q_locations
WHERE standalone = NEW.standalone);
END;
Update: So with help I got, I managed to insert trigger without no errors, bu when I update my locations table, trigger doesn't do anything. Values just keep incrementing as default.
Try:
SET NEW.id = (SELECT coalesce(...) ...)
Maybe SELECT INTO isn't working properly in Update Triggers with NEW-Aliases.
UPDATE:
This should work:
DELIMITER $$
CREATE TRIGGER trigger_insert_q_locations
BEFORE INSERT ON q_locations
FOR EACH ROW
BEGIN
DECLARE currentid INT;
SET currentid = (SELECT max(id) FROM q_locations WHERE standalone = NEW.standalone);
IF NEW.standalone = 0 THEN
SET NEW.id = coalesce(currentid + 1, 1);
ELSE
SET NEW.id = coalesce(currentid + 1,1000);
END IF;
END;
Can somebody please show me how generate a sequence in MySQL 5.1?
create table users
(
user_id int unsigned not null auto_increment primary key,
username varbinary(32) unique not null
...
)
engine=innodb;
you could also do
drop table if exists users_seq;
create table users_seq
(
next_val int unsigned not null default 0
)
engine = innodb;
insert into users_seq values (0);
drop table if exists users;
create table users
(
user_id int unsigned not null primary key,
username varbinary(32) unique not null
)
engine=innodb;
delimiter #
create trigger users_before_ins_trig before insert on users
for each row
begin
declare v_id int unsigned default 0;
select next_val+1 into v_id from users_seq;
set new.user_id = v_id;
update users_seq set next_val = v_id;
end#
delimiter ;
insert into users (username) values ('f00'),('foo'),('bar');
select * from users;
select next_val from users_seq;
MySQL doesn't have sequences, however it does have the auto-increment feature.
http://www.experts123.com/q/does-mysql-5.1-have-sequences.html
CREATE TABLE test (
customer_id bigint(21) NOT NULL PRIMARY KEY AUTO_INCREMENT
);