MySQL: Multiple Primary Keys and Auto Increment - mysql

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.

Related

MySQL how to create a custom id column formatted with character, date and number

CREATE TABLE customers (
id INT NOT NULL PRIMARY KEY AUTO_INCREMENT...
I need the sequence to go like so:
KR-21-001
KR-21-002
KR-21-003
Explanation
KR - Is Stable Character
21 - Todays year date format (yy)
001 - Increments by +1 when a new value is added.
Next year (2022)..
the sequence needs to be reset back to 001 but the 20 changes to 21 because of the year being 2022 :
KR-22-001
KR-22-002
KR-22-003
Question
How can I create this custom ID column BEFORE INSERT?
What you are requesting is actually somewhat more complicated.
You can't use auto genrated columns and for A BEFORE Trigger NEW.id is empty
As you complicate more and more you need to add a column to grab the change i year
But you could do something like this
CREATE TABLE customers (
id INT NOT NULL PRIMARY KEY AUTO_INCREMENT
,custom_id VARCHAR(100)
,test varchar(10)
)
CREATE TABLE customers (
id INT NOT NULL PRIMARY KEY AUTO_INCREMENT
,Newid INT
,created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
,custom_id VARCHAR(100)
,test varchar(10)
)
CREATE TRIGGER after_customers_insert
BEFORE INSERT
ON customers FOR EACH ROW
BEGIN
SET #created := (SELECT IFNULL(created_at, NOW()) FROM customers ORDER BY created_at DESC LIMIT 1);
IF YEAR(#created) <> YEAR(NEw.created_at) THEN
SET NEW.Newid := 1;
ELSE
SET NEW.Newid := (SELECT IFNULL(MAX(Newid),0) + 1 FROM customers);
END IF;
SET NEW.custom_id = CONCAT('KR_',DATE_FORMAT(now(), '%y'),'_',LPAD(NEW.Newid,3,'0'));
END
INSERT INTO customers (test) VALUEs ('test')
INSERT INTO customers (test) VALUEs ('test')
UPDATE customers SET created_at = created_at - INTERVAL 1 YEAR
INSERT INTO customers (test) VALUEs ('test')
SELECT * FROM customers
id | Newid | created_at | custom_id | test
-: | ----: | :------------------ | :-------- | :---
1 | 1 | 2020-07-30 22:10:30 | KR_21_001 | test
2 | 2 | 2020-07-30 22:10:30 | KR_21_002 | test
3 | 1 | 2021-07-30 22:10:30 | KR_21_001 | test
db<>fiddle here

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.

How to make assign always the value 1 to the LAST "row_id" created

I have a mysql table and the first column names "row_id" and contains an number that shows the id of the row created into the table.
Here below a very simple an example made easier:
+--------+---------+------+---------+------+-----------+
| row_id | user_id | city | zipcode | note | imagepath |
+--------+---------+------+---------+------+-----------+
| 1 | | | | | |
| 2 | | | | | |
| 3 | | | | | |
| 4 | | | | | |
+--------+---------+------+---------+------+-----------+
I would like to know if there is a way, into the phpmyadmin console, to be able to make assign always the value 1 to the LAST "row_id" created, making become as 2 the previous "row_id" that was 1, making become as 3 the previous "row_id" that was 2, and so on.
Thank you very much.
Simply:
UPDATE my_table SET row_id = row_id + 1; INSERT INTO my_table VALUES (1, 5, 'fourth_city', 'fourth_zipcode', 'fourth_note', 'fourth_image');
UPDATE my_table SET row_id = row_id + 1; INSERT INTO my_table VALUES (1, 6, 'fifth_city', 'fifth_zipcode', 'fifth_note', 'fifth_image');
.... And so on
But if row_id is your primary key then you must drop it before:
ALTER TABLE my_table DROP PRIMARY KEY;
UPDATE my_table SET row_id = row_id + 1; INSERT INTO my_table VALUES (1, 5, 'fourth_city', 'fourth_zipcode', 'fourth_note', 'fourth_image');
ALTER TABLE my_table ADD PRIMARY KEY(row_id);
ALTER TABLE my_table DROP PRIMARY KEY;
UPDATE my_table SET row_id = row_id + 1; INSERT INTO my_table VALUES (1, 6, 'fifth_city', 'fifth_zipcode', 'fifth_note', 'fifth_image');
ALTER TABLE my_table ADD PRIMARY KEY(row_id);
.... And so on.
You can create a stored procedure or function to simplify the call. Hope help.

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

MySQL Alter table, add column with unique random value

I have a table that I added a column called phone - the table also has an id set as a primary key that auto_increments. How can I insert a random value into the phone column, that won't be duplicated. The following UPDATE statement did insert random values, but not all of them unique. Also, I'm not sold I cast the phone field correctly either, but ran into issues when trying to set it as a int(11) w/ the ALTER TABLE command (mainly, it ran correctly, but when adding a row with a new phone number, the inserted value was translated into a different number).
UPDATE Ballot SET phone = FLOOR(50000000 * RAND()) + 1;
Table spec's
+------------+--------------+------+-----+---------+----------------+
| Field | Type | Null | Key | Default | Extra |
+------------+--------------+------+-----+---------+----------------+
| id | int(11) | NO | PRI | NULL | auto_increment |
| phone | varchar(11) | NO | | NULL | |
| age | tinyint(3) | NO | | NULL | |
| test | tinyint(4) | NO | | 0 | |
| note | varchar(100) | YES | | NULL | |
+------------+--------------+------+-----+---------+----------------+
-- tbl_name: Table
-- column_name: Column
-- chars_str: String containing acceptable characters
-- n: Length of the random string
-- dummy_tbl: Not a parameter, leave as is!
UPDATE tbl_name SET column_name = (
SELECT GROUP_CONCAT(SUBSTRING(chars_str , 1+ FLOOR(RAND()*LENGTH(chars_str)) ,1) SEPARATOR '')
FROM (SELECT 1 /* UNION SELECT 2 ... UNION SELECT n */) AS dummy_tbl
);
-- Example
UPDATE tickets SET code = (
SELECT GROUP_CONCAT(SUBSTRING('123abcABC-_$#' , 1+ FLOOR(RAND()*LENGTH('123abcABC-_$#')) ,1) SEPARATOR '')
FROM (SELECT 1 UNION SELECT 2 UNION SELECT 3 UNION SELECT 4 UNION SELECT 5) AS dummy_tbl
);
Try this
UPDATE Ballot SET phone = FLOOR(50000000 * RAND()) * id;
I'd tackle this by generating a (temporary) table containing the numbers in the range you need, then looping through each record in the table you wish to supply with random numbers. Pick a random element from the temp table, update the table with that, and remove it from the temp table. Not beautiful, nor fast.. but easy to develop and easy to test.