MYSQL Table creation with default value(expression) to a column - mysql

I have a Table Employee(id,name,dept_name).I want the id will alphanumeric [dddddaaaaa] with first 5 digit will be auto increment id and rest 4 char will be the first 4 char of employee name.
For example , for the first employee name=John Todd ,the auto incremented part of the Id will be 00001. And so the Id will be 00001JOHN.
Is it possible to set a default expression to the column Id=(concat(autoincrement,substring(name,4)).
I was also thinking if I Can create a trigger on after insert Employee and the trigger will update the Employee.Id. But MySql does not allow to update the same table from trigger for which trigger got fired.
Please Help.

What about a schema like
CREATE TABLE employee
(
employeeid INT PRIMARY KEY AUTO_INCREMENT,
firstname varchar(255)
);
CREATE INDEX part_of_firstname ON employee (firstname(4));
That'll let you perform lookups fairly quickly using your natural primary key, while giving you an artificial primary key and not forcing to denormalize.
EXPLAIN SELECT * FROM EMPLOYEE WHERE EMPLOYEEID = 1 AND FIRSTNAME LIKE 'john%';
+----+-------------+----------+-------+---------------------------+---------+---------+-------+------+-------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+-------------+----------+-------+---------------------------+---------+---------+-------+------+-------+
| 1 | SIMPLE | employee | const | PRIMARY,part_of_firstname | PRIMARY | 4 | const | 1 | |
+----+-------------+----------+-------+---------------------------+---------+---------+-------+------+-------+
Of course since the 0001 part of the primary key is unique enough to identify the user you need not query the name at all.
If you insist on precalculating this should work
CREATE TABLE employee
(
employeeid INT PRIMARY KEY AUTO_INCREMENT,
specialid VARCHAR(255),
firstname VARCHAR(255)
);
CREATE INDEX employee_specialid ON employee (firstname(4));
DELIMITER ;;
CREATE TRIGGER employeeid_trigger BEFORE insert ON employee
FOR EACH ROW
BEGIN
SET new.specialid = CONCAT(LPAD((SELECT AUTO_INCREMENT FROM information_schema.TABLES WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'employee'), 4, '0'), SUBSTRING(new.firstname, 1, 4));
END
;;
DELIMITER ;
Testing it:
mysql> insert into employee (firstname) values ('johnathan');
Query OK, 1 row affected (0.04 sec)
mysql> insert into employee (firstname) values ('johnathan');
Query OK, 1 row affected (0.02 sec)
mysql> insert into employee (firstname) values ('johnathan');
Query OK, 1 row affected (0.02 sec)
mysql> select * from employee;
+------------+-----------+-----------+
| employeeid | specialid | firstname |
+------------+-----------+-----------+
| 1 | 0001john | johnathan |
| 2 | 0002john | johnathan |
| 3 | 0003john | johnathan |
+------------+-----------+-----------+
3 rows in set (0.00 sec)
This is kind of a hack, and information_schema won't be available on some DBs where permissions aren't under your control.

You could try concatenating it in your select statement instead of storing an auto increment column and an id column,
SELECT CONCAT(id, substring(name,4) FROM tbl_employee
That way you wouldn't need triggers

Related

MySQL reorder row id by date

SELECT time
FROM posts
ORDER BY time ASC;
This will order my posts for me in a list. I would like to reorder the table itself making sure that there are no missing table ids. Thus, if I delete column 2, I can reorder so that row 3 will become row 2.
How can I do this? Reorder a table by its date column so there is always an increment of 1, no non-existing rows.
Disclaimer: I don't really know why you would need to do it, but if you do, here is just one of many ways, fairly independent of the engine or the server version.
Setup:
CREATE TABLE t (
`id` int(11) NOT NULL AUTO_INCREMENT,
`time` time DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB;
INSERT INTO t (`time`) VALUES ('13:00:00'),('08:00:00'),('02:00:00');
DELETE FROM t WHERE id = 2;
Initial condition:
SELECT * FROM t ORDER BY `time`;
+----+----------+
| id | time |
+----+----------+
| 3 | 02:00:00 |
| 1 | 13:00:00 |
+----+----------+
2 rows in set (0.00 sec)
Action:
CREATE TRIGGER tr AFTER UPDATE ON t FOR EACH ROW SET #id:=#id+1;
ALTER TABLE t ADD COLUMN new_id INT NOT NULL AFTER id;
SET #id=1;
UPDATE t SET new_id=#id ORDER BY time;
DROP TRIGGER tr;
Result:
SELECT * FROM t ORDER BY `time`;
+----+--------+----------+
| id | new_id | time |
+----+--------+----------+
| 3 | 1 | 02:00:00 |
| 1 | 2 | 13:00:00 |
+----+--------+----------+
2 rows in set (0.00 sec)
Cleanup:
Further you can do whatever is more suitable for your case (whatever is faster and less blocking, depending on other conditions). You can update the existing id column and then drop the extra one:
UPDATE t SET id=new_id;
ALTER TABLE t DROP new_id;
SELECT * FROM t ORDER BY `time`;
+----+----------+
| id | time |
+----+----------+
| 1 | 02:00:00 |
| 2 | 13:00:00 |
+----+----------+
2 rows in set (0.00 sec)
Or you can drop the existing id column and promote new_id to the primary key.
Comments:
A natural variation of the same approach would be to wrap it into a stored procedure. It's basically the same, but requires a bit more text. The benefit of it is that you could keep the procedure for the next time you need it.
Assuming you have a unique index on id, a temporary column new_id is needed in a general case, because if you start updating id directly, you can get a unique key violation. It shouldn't happen if your id is already ordered properly, and you are only removing gaps.

Creating a trigger on insert on a table, that creates new rows on another table, according to amount of row on another (3rd) table

Assume the following structure:
Table1:
ID | name
Table2:
ID | name
Table3:
ID | table1_id | table2_id | value
I want to build a trigger, after insert to Table1 if id not exist, to create new rows for each row in Table2 inside Table3 with the corresponding IDs.
What I did so far is creating this logic in php, I have never created triggers this complex before so I don't really know how to approach this.
Example:
Customers Table after insert:
+----+------+
| ID | Name |
+----+------+
| 1 | Dan |
+----+------+
Currency Table:
+----+------+
| ID | Name |
+----+------+
| 1 | USD |
| 2 | EUR |
+----+------+
Customers Currency Table after trigger
+----+---------------+-------------+-------+
| ID | customer_id | currency_id | Value |
+----+---------------+-------------+-------+
| 1 | 1 | 1 | NULL |
| 2 | 1 | 2 | NULL |
+----+---------------+-------------+-------+
Another option that you can use is:
DELIMITER $$
DROP TRIGGER /*!50032 IF EXISTS */ `trg_bi`$$
CREATE TRIGGER `trg_bi` BEFORE INSERT ON `table1`
FOR EACH ROW
BEGIN
INSERT INTO `table3` (`table1_id`, `table2_id`)
SELECT NEW.`id`, `t2`.`id`
FROM `table2` `t2`
WHERE NOT EXISTS (SELECT NULL FROM `table1` `t1` WHERE `t1`.`id` = NEW.`id`);
END$$
DELIMITER ;
Here is validated by the id column table1, but you can use the column you want to validate, however, depends as validate that there is no 'customer' in table1.
SQL Fiddle example
To deal with this you need to use cursor in trigger, here is a nice tutorial on this http://www.mysqltutorial.org/mysql-cursor/
Now in your case I would suggest that the customer table id should be primary key auto incremented so that you always have unique value
So here how it should be
create table customer (id int primary key auto_increment , name varchar (100));
create table currency (id int primary key auto_increment, name varchar(100));
insert into currency (name) values ('USD'),('EUR') ;
create table customer_currency (id int primary key auto_increment, customer_id int , currency_id int , val varchar(100));
The trigger will be something as
delimiter //
create trigger customer_add after insert on customer
for each row
begin
DECLARE done INT DEFAULT FALSE;
DECLARE currency_id int;
DECLARE currency_val varchar(100);
DECLARE cur CURSOR FOR SELECT id,name FROM currency;
DECLARE CONTINUE HANDLER FOR NOT FOUND SET done = TRUE;
OPEN cur;
ins_loop: LOOP
FETCH cur INTO currency_id,currency_val;
IF done THEN
LEAVE ins_loop;
END IF;
INSERT INTO customer_currency (customer_id,currency_id,val) VALUES (NEW.id,currency_id,currency_val);
END LOOP;
CLOSE cur;
end ; //
delimiter ;
Now in mysql lets add a record on customer table
mysql> insert into customer (name) values ('Abhik') ;
Query OK, 1 row affected (0.02 sec)
Now lets see what is there in the customer_currency symbol
mysql> select * from customer_currency ;
+----+-------------+-------------+------+
| id | customer_id | currency_id | val |
+----+-------------+-------------+------+
| 1 | 1 | 1 | USD |
| 2 | 1 | 2 | EUR |
+----+-------------+-------------+------+
In the trigger I am adding the currency value as well in the 3rd table if you do not want then can ignore that and it will become null.
You can write an after delete trigger on customer and delete the data from customer_currency where customer_id is the id of the deleted row in customer table.

MySQL: Multiple Primary Keys and Auto Increment

I'm quite new to setting up tables in MySQL and there is something I'd like to do which is a bit more advance than I'm able to do.
I have two columns as part of a composite primary key, one is a Date and an ID I would like to be an auto increment integer. For each date, I would like to reset the auto integer to 0, so something like this:
|-----------------|
|Date | ID |
|-----------------|
|2012-06-18 | 1 |
|2012-06-18 | 2 |
|2012-06-18 | 3 |
|2012-06-19 | 1 |
|2012-06-19 | 2 |
|2012-06-20 | 1 |
|-----------------|
Thanks
Here this should work.
CREATE TABLE `answer`(
`dates` DATE NOT NULL,
`id` mediumint(9) NOT NULL AUTO_INCREMENT,
PRIMARY KEY (`dates`,`id`)
) ENGINE=MyISAM;
It is known to cause problems with innoDB. Hope this helps you.
EDIT: RESULTS
2012-06-19 1
2012-06-19 2
2012-06-19 3
2012-07-19 1
2012-07-19 2
2012-08-19 1
On php myadmin.
Well, for me mysql does what you want automatically.
mysql> CREATE TABLE TestData(Date date not null, ID int unsigned not null auto_increment, PRIMARY KEY(Date, ID));
mysql> INSERT INTO TestData SET Date = "2012-06-18";
mysql> INSERT INTO TestData SET Date = "2012-06-18";
mysql> INSERT INTO TestData SET Date = "2012-06-18";
mysql> INSERT INTO TestData SET Date = "2012-06-19";
mysql> INSERT INTO TestData SET Date = "2012-06-19";
mysql> INSERT INTO TestData SET Date = "2012-06-20";
mysql> select * from TestData;
+------------+----+
| Date | ID |
+------------+----+
| 2012-06-18 | 1 |
| 2012-06-18 | 2 |
| 2012-06-18 | 3 |
| 2012-06-19 | 1 |
| 2012-06-19 | 2 |
| 2012-06-20 | 1 |
+------------+----+
No magic involved.
You can create a before insert trigger.
DELIMITER $$
CREATE TRIGGER `composite_auto_increment` BEFORE INSERT ON `your_table`
FOR EACH ROW
BEGIN
DECLARE max_id INT(11); -- add the appropriate column length from your table definition
SELECT ID FROM `your_table` WHERE `Date` = DATE(NOW()) INTO max_id;
SET NEW.ID = IF(ISNULL(max_id), 1, max_id + 1);
END$$
This way, if and ID already existed for the day, it gets incremented. If it didn't, it gets set to 1. Note that in this scenario, ID isn't AUTO_INCREMENT in the table definition. It just gets done by the trigger.
In trigger:
SELECT ID FROM your_table WHERE Date = DATE(NOW()) INTO max_id;
must be:
SELECT max(ID) FROM your_table WHERE Date = NEW.key_field INTO max_id;
but better is lock by key.
this is better for concurrent inserts on innodb.

Final Part of Cursor inserting twice into MYSQL Using Trigger

I'm using a trigger in MySQL to do the following:
When I add a new client to the client table, it should create a set of entries in a 'Client-Type' table, linking the client id to a set of type ids (client1, type1 client1, type2) etc...
However, the database is inserting the entry for the last type twice when the trigger is run. So the last two entries are (client1, type9 client1, type9).
The trigger code is as follows:
AFTER INSERT ON `nmsTicket`.`client`
FOR EACH ROW
BEGIN
DECLARE done BOOLEAN DEFAULT 0;
DECLARE a CHAR(2);
DECLARE types CURSOR
FOR
SELECT typeID FROM type;
DECLARE CONTINUE HANDLER FOR SQLSTATE '02000' SET done=1;
OPEN types;
REPEAT
FETCH types INTO a;
INSERT INTO clientType(id_type, id_client) VALUES (a,new.idClient);
UNTIL done END REPEAT;
CLOSE types;
I've looked it over a few times, but I can't see why it would be exhibiting this behaviour; all the entries before the last one work fine.
Any pointers?
I'm not sure why you're using a cursor in your trigger - this should be avoided unless you absolutely need one (see here for example Optimal MySQL settings for queries that deliver large amounts of data?)
The following is a simplified example (minus referential integrity) which uses a cross join instead of a cursor. In addition you'll notice I'm not using a surrogate primary key on the client_types table but a composite one instead which better enforces data integrity.
Schema
drop table if exists client_type; --your type table
create table client_type
(
type_id tinyint unsigned not null auto_increment primary key,
name varchar(255) unique not null
)
engine=innodb;
drop table if exists client;
create table client
(
client_id int unsigned not null auto_increment primary key,
name varchar(255) not null
)
engine=innodb;
drop table if exists client_types; -- your clienttype table
create table client_types
(
client_id int unsigned not null,
type_id tinyint unsigned not null,
primary key (client_id, type_id) -- ** note use of composite primary key **
)
engine=innodb;
delimiter #
create trigger client_after_ins_trig after insert on client
for each row
begin
insert into client_types (client_id, type_id)
select
c.client_id,
ct.type_id
from
client c
cross join client_type ct
where
c.client_id = new.client_id
order by
ct.type_id;
end#
delimiter ;
Testing
mysql> insert into client_type (name) values ('type one'),('type two'),('type three');
Query OK, 3 rows affected (0.03 sec)
mysql> insert into client (name) values ('client A'),('client B');
Query OK, 2 rows affected (0.04 sec)
mysql> select * from client_type;
+---------+------------+
| type_id | name |
+---------+------------+
| 1 | type one |
| 3 | type three |
| 2 | type two |
+---------+------------+
3 rows in set (0.00 sec)
mysql> select * from client;
+-----------+----------+
| client_id | name |
+-----------+----------+
| 1 | client A |
| 2 | client B |
+-----------+----------+
2 rows in set (0.00 sec)
mysql> select * from client_types;
+-----------+---------+
| client_id | type_id |
+-----------+---------+
| 1 | 1 |
| 1 | 2 |
| 1 | 3 |
| 2 | 1 |
| 2 | 2 |
| 2 | 3 |
+-----------+---------+
6 rows in set (0.00 sec)
Hope this helps :)

How can I set an AUTO_INCREMENT field to start with the value 6000 in MySQL?

How can I set a non-auto-increment field to auto increment in MySQL? And how can I set the auto increment start value to 6000?
... how set a field auto increment with start value 6000 in mysql?
If your table already exists:
ALTER TABLE your_table AUTO_INCREMENT = 6000;
If you are creating your table from scratch:
CREATE TABLE your_table () AUTO_INCREMENT = 6000;
Source and further reading:
MySQL 5.1 Reference Manual :: Using AUTO_INCREMENT
Test case:
CREATE TABLE users (
user_id int NOT NULL,
name varchar(50),
PRIMARY KEY (user_id)
);
INSERT INTO users VALUES (1, 'Bob');
INSERT INTO users VALUES (2, 'Joe');
INSERT INTO users VALUES (3, 'Paul');
ALTER TABLE users MODIFY user_id int NOT NULL AUTO_INCREMENT;
ALTER TABLE users AUTO_INCREMENT = 6000;
INSERT INTO users (name) VALUES ('Keith');
INSERT INTO users (name) VALUES ('Steve');
INSERT INTO users (name) VALUES ('Jack');
SELECT * FROM users;
+---------+-------+
| user_id | name |
+---------+-------+
| 1 | Bob |
| 2 | Joe |
| 3 | Paul |
| 6000 | Keith |
| 6001 | Steve |
| 6002 | Jack |
+---------+-------+
6 rows in set (0.01 sec)
ALTER TABLE tbl_name AUTO_INCREMENT = 6000
but be aware you should have no PK lager 6000 in this table !
mysql will show you the correct syntax for this, and more, if you execute the following for a table that contains an auto increment PK & some data already:
SHOW CREATE TABLE your_tablename;