how to design a table in mysql with two auto_increment fields - mysql

For a project we do the database design at the moment. We think we should use two auto_increment fields in one table.
table master:
`pid` int(10) NOT NULL auto_increment,
`iid` int(10) NOT NULL auto_increment,
...
To start with a alternate auto_incremet you can use ALTER TABLE tbl AUTO_INCREMENT = 100000; This will work only for the whole table 'tbl'.
auto_increment for pid should be 50000000 and auto_increment for iid should be 80000000
We want to avoid splitting it into 3 tables with relations master -> table.pid and master -> table.iid.
altering the table is not working cause
/* SQL Error (1075): Incorrect table definition; there can be only one auto column and it must be defined as a key */
Is it possible or what alternative do you recommend?

If you cannot use two auto columns I think you must redesign your database. What do you need exactly?

I dont fully understand your question but you can use triggers to maintain key values like the following:
drop table if exists grid;
create table grid
(
grid_id int unsigned not null auto_increment primary key,
name varchar(255) not null,
next_token_id int unsigned not null default 0,
next_node_id int unsigned not null default 0
)
engine = innodb;
drop table if exists grid_token;
create table grid_token
(
grid_id int unsigned not null,
token_id int unsigned not null,
name varchar(255) not null,
primary key (grid_id, token_id) -- note clustered PK order (innodb only)
)
engine = innodb;
drop table if exists grid_node;
create table grid_node
(
grid_id int unsigned not null,
node_id int unsigned not null,
name varchar(255) not null,
primary key (grid_id, node_id) -- note clustered PK order (innodb only)
)
engine = innodb;
-- TRIGGERS
delimiter #
create trigger grid_token_before_ins_trig before insert on grid_token
for each row
begin
declare tid int unsigned default 0;
select next_token_id + 1 into tid from grid where grid_id = new.grid_id;
set new.token_id = tid;
update grid set next_token_id = tid where grid_id = new.grid_id;
end#
create trigger grid_node_before_ins_trig before insert on grid_node
for each row
begin
declare nid int unsigned default 0;
select next_node_id + 1 into nid from grid where grid_id = new.grid_id;
set new.node_id = nid;
update grid set next_node_id = nid where grid_id = new.grid_id;
end#
delimiter ;
-- TEST DATA
insert into grid (name) values ('g1'),('g2'),('g3');
insert into grid_token (grid_id, name) values
(1,'g1 t1'),(1,'g1 t2'),(1,'g1 t3'),
(2,'g2 t1'),
(3,'g3 t1');
insert into grid_node (grid_id, name) values
(1,'g1 n1'),(1,'g1 n2'),
(2,'g2 n1'),
(3,'g3 n1'),(3,'g3 n2');
select * from grid;
select * from grid_token;
select * from grid_node;

Related

MySQL create table and trigger

Can anyone help me understand what this code does?
CREATE TABLE `exams` (
`id` int(11) NOT NULL, `score` int(2) NOT NULL, `class` int(11) NOT NULL, `date` date NOT NULL, PRIMARY KEY (`class`,`date`,`id`));
CREATE TRIGGER `trig3` BEFORE INSERT ON `exams` FOR EACH ROW SET #xxx = #xxx + 1;
SET #xxx = 0;
INSERT INTO `exams` VALUES (10001,24,1,'2013-09-16'), (10005,30,1,'2013-09-16'), (10083,30,1,'2014-03-21'), (10120,26,1,'2014-03-21'), (10035,23,1,'2014-07-22'), (10005,28,2,'2014-01-23'), (10001,27,2,'2014-06-30'), (10001,25,4,'2014-01-23'), (10083,22,4,'2014-01-23'), (10120,30,4,'2014-01-23'), (10005,22,4,'2014-03-21');
SELECT #xxx;
I understand the creation of the table and the insertion of values but i don't get the rest of it.
The code seams to be a workaround way to count or check if the multi insert inserted all records or not.
SELECT #xxx;
| #xxx |
| ---- |
| 11 |
But MySQL doesn't have a native way to find that out.
see demo
A more easy MySQL code to do the same without needing the TRIGGER code and SET.
But it will require a new table structure to function.
CREATE TABLE `esami` (
`id` INT NOT NULL AUTO_INCREMENT PRIMARY KEY
, `id_studente` int(11) NOT NULL
, `voto` int(2) NOT NULL
, `id_corso` int(11) NOT NULL
, `data` date NOT NULL
, UNIQUE KEY (`id_corso`,`data`,`id_studente`)
);
And the query to test if all records where inserted by the multi insert.
SELECT
COUNT(*)
FROM
esami
WHERE
id BETWEEN LAST_INSERT_ID() AND LAST_INSERT_ID() + 11;
see demo

Update row in first table and insert a row into a second table in a single query

I'm trying to figure it out how to insert a row into a table on updating a particular field in the second table.
Let's say I have table 1 (dif):
CREATE TABLE dif
(
Position INT(10) UNSIGNED PRIMARY KEY NOT NULL AUTO_INCREMENT,
pKey SMALLINT(3) UNSIGNED NOT NULL,
Number SMALLINT(3) UNSIGNED DEFAULT 0 NOT NULL
);
ALTER TABLE dif
ADD CONSTRAINT dif_article_pKey_fk
FOREIGN KEY (pKey) REFERENCES article (pKey) ON UPDATE CASCADE;
and table 2 (article):
CREATE TABLE IF NOT EXISTS article (
pKey smallint(3) unsigned NOT NULL AUTO_INCREMENT,
Name varchar(80) COLLATE utf8_roman_ci NOT NULL,
Number SMALLINT NOT NULL DEFAULT 0
PRIMARY KEY (pKey)
);
The table article is populated with some data and should be only updated. Table "dif" is empty at the beginning. So, let's say I'm updating the fields on "article" like this:
UPDATE article SET pKey = 15, Name = SomeName, Number = 22 WHERE pKey=15;
Can I somehow combine the UPDATE query with this?
INSERT INTO dif (pKey, Number) VALUES (15, 12);
The "12" is the difference between the "article.Number" before and after UPDATE.
No, but you can make a stored procedure that does both of those things and then execute it in a single statement.
create procedure GiveThisABetterName
(
in pKey int,
in newNumber int,
in currentNumber int,
in newName varchar(100)
)
begin
update
article
set
Name = newName, Number = newNumber
where
pKey = pKey;
insert into dif (pKey, Number) values (pKey, newNumber);
end
My mysql syntax is rusty, but that should be close. Then when you want to execute it:
call GiveThisABetterName(12, 15, 22, 'Some Name');
EDIT: After reading your question again, it seems to me that you're trying to make your data model track audit information that it's just not set up to accommodate naturally. Do you have control over the model? If so, consider something like this (see here for a working example of what's below):
CREATE TABLE IF NOT EXISTS article (
pKey smallint(3) unsigned NOT NULL AUTO_INCREMENT,
Name varchar(80) COLLATE utf8_roman_ci NOT NULL,
PRIMARY KEY (pKey)
);
CREATE TABLE ArticleNumbers
(
Counter int UNSIGNED PRIMARY KEY AUTO_INCREMENT,
pKey SMALLINT(3) UNSIGNED NOT NULL,
Number SMALLINT(3) DEFAULT 0 NOT NULL,
Difference SMALLINT(3)
);
ALTER TABLE ArticleNumbers
ADD CONSTRAINT ArticleNumbers_article_pKey_fk
FOREIGN KEY (pKey) REFERENCES article (pKey) ON UPDATE CASCADE;
Maybe add a few views to make things easier:
CREATE VIEW GroupedArticleNumbers
as
select pKey, max(Counter) as Counter
from ArticleNumbers
group by pKey;
CREATE VIEW CurrentArticles
as
select article.pKey, article.Name, numbers.Number, numbers.Difference
from article
left outer join GroupedArticleNumbers filter on article.pKey = filter.pKey
left outer join ArticleNumbers numbers on filter.Counter = numbers.Counter;
Since you can track the number separately from the base record now but still easily determine what the current number is, you can now combine your update and insert statement functionality. See below.
First, some test data:
insert into article (Name) values ('Test');
insert into ArticleNumbers (pKey, Number, Difference) values (1, 10, null);
insert into ArticleNumbers (pKey, Number, Difference) select 1, 20, 20 - Number from CurrentArticles where pKey = 1;
insert into ArticleNumbers (pKey, Number, Difference) select 1, 50, 50 - Number from CurrentArticles where pKey = 1;
insert into ArticleNumbers (pKey, Number, Difference) select 1, 15, 15 - Number from CurrentArticles where pKey = 1;
See how nicely that works out once the overhead of setting up the schema has been done?
To get the current number for the article we created:
select * from currentarticles where pKey = 1
To get the number history for that article:
select * from article
left outer join articlenumbers on article.pkey = articlenumbers.pkey
order by counter asc
If you're willing to mess with your data model, you can have an alternative to stored procedures.
Alternatively, if you want to use triggers as #Jonathan Leffler suggested, something like this should work:
CREATE TABLE article (
pKey smallint(3) unsigned NOT NULL AUTO_INCREMENT,
Name varchar(80) COLLATE utf8_roman_ci NOT NULL,
Number SMALLINT(3) DEFAULT 0 NOT NULL,
PRIMARY KEY (pKey)
);
CREATE TABLE ArticleNumbers
(
Counter int UNSIGNED PRIMARY KEY AUTO_INCREMENT,
pKey SMALLINT(3) UNSIGNED NOT NULL,
Number SMALLINT(3) DEFAULT 0 NOT NULL,
Difference SMALLINT(3)
);
delimiter $
create trigger tr_u_article
before update on article
for each row
begin
insert into ArticleNumbers (pKey, Number, Difference) select old.pKey, new.Number, new.Number - old.Number
end;
delimiter ;

How to Add integer column to an String column in MySQl 5.0

I Want to add an Integer Column to a String that's because i need to generate a varchar variable with a numeric part that automatically increments. For example, P000001,P000002...
In order to do that what i am doing while creation of table i have taken an int field ID which auto_increments and i am Concatenating P with 00000 and the ID value
The Table i have created is :
CREATE TABLE tblAcceptTest(
ID int AUTO_INCREMENT NOT NULL primary key,
PatientID as CONCAT('P' , CONCAT('000000',CAST(ID as char)))
);
It Shows me the error from as keyword.
Please help
MySQL's documentation (http://dev.mysql.com/doc/refman/5.1/en/create-table.html) says, "the default value must be a constant; it cannot be a function or an expression." Why don't you just get the PatientID value afterward as part of the SELECT:
SELECT CONCAT('P', LPAD(ID, 6, 0)) AS PatientID FROM tblAcceptTest;
It looks like you want six digits after the "P", so try this for your expression:
CONCAT('P', LPAD(ID, 6, '0'))
Mysql has little support for computed columns.
Patient ID from your specification could be a char(7)
CREATE TABLE tblAcceptTest(
ID int AUTO_INCREMENT NOT NULL primary key,
PatientID char(7)
);
Then create some triggers. Note that the following insert trigger will cause issues with high concurrency servers.
DELIMITER |
CREATE TRIGGER tblAcceptTest_insert BEFORE INSERT ON tblAcceptTest
FOR EACH ROW BEGIN
DECLARE next_id INT;
SET next_id = (SELECT AUTO_INCREMENT FROM information_schema.TABLES WHERE TABLE_SCHEMA=DATABASE() AND TABLE_NAME='tblAcceptTest');
SET NEW.PatientID = CONCAT('P' , RIGHT(CONCAT('000000',next_id),6)) ;
END;
|
CREATE TRIGGER tblAcceptTest_update BEFORE UPDATE ON tblAcceptTest
FOR EACH ROW BEGIN
SET NEW.PatientID = CONCAT('P' , RIGHT(CONCAT('000000',NEW.ID),6)) ;
END;
|
DELIMITER ;
You use relationships and views to achieve the same result.
CREATE TABLE `patient` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`patient` varchar(60) DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
CREATE TABLE `accepted_test` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`patient_id` int(11) NOT NULL,
`accepted` timestamp NULL DEFAULT NULL,
PRIMARY KEY (`id`),
KEY `patient_id` (`patient_id`),
CONSTRAINT `accepted_test_ibfk_1` FOREIGN KEY (`patient_id`) REFERENCES `patient` (`id`) ON DELETE CASCADE ON UPDATE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
create or replace view accepted_test_veiw as
select CONCAT('P' , RIGHT(CONCAT('000000',patient_id),6)) patient_key
, accepted
, id accepted_test_id
, patient_id
from accepted_test ;
select * from `accepted_test_veiw`

How to generate sequence in MySQL 5.1community edition

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

Mysql - second auto_increment col with diff behaviour

I have a proposals table like this
- ID (auto_increment)
- proposal_id
- client_id
There a way in sql that the proposal_id increments just for each client_id
example:
ID proposal_id client_id
1 1 1
2 1 2
3 2 1
4 3 1
5 2 2
6 3 2
i know i can get the last poposal_id and +1 and i add the new entry... but i dont want to do a sql instruction just to get this value... instead i want to use in a sql!
Tkz
Roberto
As I understand you wish to have proposal_id as a sequence in a continuos manner per client_id. Either you should normalize the table to split into per-client-table [tricky and not advisable] to do this or write a SELECT
I think this is what you want if using innodb (recommended) although you can simplify this with myisam
delimiter ;
drop table if exists customer;
create table customer(
cust_id int unsigned not null auto_increment primary key,
name varchar(255) unique not null,
next_proposal_id smallint unsigned not null default 0
)engine = innodb;
insert into customer (name) values ('c1'),('c2'),('c3');
drop table if exists proposal;
create table proposal(
cust_id int unsigned not null,
proposal_id smallint unsigned not null,
proposal_date datetime not null,
primary key (cust_id, proposal_id) -- composite clustered primary key
)engine=innodb;
delimiter #
create trigger proposal_before_ins_trig before insert on proposal for each row
begin
declare new_proposal_id smallint unsigned default 0;
select next_proposal_id+1 into new_proposal_id from customer
where cust_id = new.cust_id;
update customer set next_proposal_id = new_proposal_id where cust_id = new.cust_id;
set new.proposal_id = new_proposal_id;
set new.proposal_date = now();
end#
delimiter ;
insert into proposal (cust_id) values (1),(2),(1),(3),(2),(1),(1),(2);
select * from proposal;
select * from customer;
hope it helps :)
i've added the myisam version below for good measure:
drop table if exists customer;
create table customer(
cust_id int unsigned not null auto_increment primary key,
name varchar(255) unique not null
)engine = myisam;
insert into customer (name) values ('c1'),('c2'),('c3');
drop table if exists proposal;
create table proposal(
cust_id int unsigned not null,
proposal_id smallint unsigned not null auto_increment,
proposal_date datetime not null,
primary key (cust_id, proposal_id) -- composite non clustered primary key
)engine=myisam;
insert into proposal (cust_id,proposal_date) values
(1,now()),(2,now()),(1,now()),(3,now()),(2,now()),(1,now()),(1,now()),(2,now());
select * from customer;
select * from proposal order by cust_id;
I think that you could design a complicated enough query to take care of this without any non-sql code, but that's not in the spirit of what you're asking. There is not a way to create the type of field-specific increment that you're asking for as a specification of the table itself.