我有多个数据库,其结构相同,有时会复制数据。为了保持数据完整性,我使用两列作为主键。一个是数据库ID,它链接到一个表,其中包含有关每个数据库的信息。另一个是表键。它不是唯一的,因为它可能有多个行,这个值相同,但在database_id列中有不同的值。
我打算将这两列放入一个联合主键。但是,我还想将表键设置为自动递增 - 但是基于database_id列。
EG,有了这些数据:
table_id database_id other_columns
1 1
2 1
3 1
1 2
2 2
如果我要添加包含dabase_id为1的数据,那么我希望table_id自动设置为4.如果输入dabase_id为2,那么我希望table_id自动设置为3. etc。
在MySql中实现此目的的最佳方法是什么。
答案 0 :(得分:42)
如果您使用的是myisam
http://dev.mysql.com/doc/refman/5.0/en/example-auto-increment.html
对于MyISAM和BDB表,您可以 在辅助节点上指定AUTO_INCREMENT 多列索引中的列。在 这种情况下,生成的值为 AUTO_INCREMENT列计算为 MAX(auto_increment_column)+ 1 WHERE 前缀=给定前缀。这很有用 当你想把数据放入有序的时候 基团。
CREATE TABLE animals (
grp ENUM('fish','mammal','bird') NOT NULL,
id MEDIUMINT NOT NULL AUTO_INCREMENT,
name CHAR(30) NOT NULL,
PRIMARY KEY (grp,id)
) ENGINE=MyISAM;
INSERT INTO animals (grp,name) VALUES
('mammal','dog'),('mammal','cat'),
('bird','penguin'),('fish','lax'),('mammal','whale'),
('bird','ostrich');
SELECT * FROM animals ORDER BY grp,id;
Which returns:
+--------+----+---------+
| grp | id | name |
+--------+----+---------+
| fish | 1 | lax |
| mammal | 1 | dog |
| mammal | 2 | cat |
| mammal | 3 | whale |
| bird | 1 | penguin |
| bird | 2 | ostrich |
+--------+----+---------+
对于你的例子:
mysql> CREATE TABLE mytable (
-> table_id MEDIUMINT NOT NULL AUTO_INCREMENT,
-> database_id MEDIUMINT NOT NULL,
-> other_column CHAR(30) NOT NULL,
-> PRIMARY KEY (database_id,table_id)
-> ) ENGINE=MyISAM;
Query OK, 0 rows affected (0.03 sec)
mysql> INSERT INTO mytable (database_id, other_column) VALUES
-> (1,'Foo'),(1,'Bar'),(2,'Baz'),(1,'Bam'),(2,'Zam'),(3,'Zoo');
Query OK, 6 rows affected (0.00 sec)
Records: 6 Duplicates: 0 Warnings: 0
mysql> SELECT * FROM mytable ORDER BY database_id,table_id;
+----------+-------------+--------------+
| table_id | database_id | other_column |
+----------+-------------+--------------+
| 1 | 1 | Foo |
| 2 | 1 | Bar |
| 3 | 1 | Bam |
| 1 | 2 | Baz |
| 2 | 2 | Zam |
| 1 | 3 | Zoo |
+----------+-------------+--------------+
6 rows in set (0.00 sec)
答案 1 :(得分:21)
这是使用innodb的一种方法,由于聚集的复合索引,它也非常高效 - 只有innodb可用...
http://dev.mysql.com/doc/refman/5.0/en/innodb-index-types.html
drop table if exists db;
create table db
(
db_id smallint unsigned not null auto_increment primary key,
next_table_id int unsigned not null default 0
)engine=innodb;
drop table if exists tables;
create table tables
(
db_id smallint unsigned not null,
table_id int unsigned not null default 0,
primary key (db_id, table_id) -- composite clustered index
)engine=innodb;
delimiter #
create trigger tables_before_ins_trig before insert on tables
for each row
begin
declare v_id int unsigned default 0;
select next_table_id + 1 into v_id from db where db_id = new.db_id;
set new.table_id = v_id;
update db set next_table_id = v_id where db_id = new.db_id;
end#
delimiter ;
insert into db (next_table_id) values (null),(null),(null);
insert into tables (db_id) values (1),(1),(2),(1),(3),(2);
select * from db;
select * from tables;
答案 2 :(得分:1)
you can make the two column primary key unique
and the auto-increment key primary
.
答案 3 :(得分:0)
DTing提供的解决方案非常出色且有效。但是当在AWS Aurora中尝试相同时,它没有起作用并且抱怨下面的错误。
Error Code: 1075. Incorrect table definition; there can be only one auto column and it must be defined as a key
因此建议基于json的解决方案。
CREATE TABLE DB_TABLE_XREF (
db VARCHAR(36) NOT NULL,
tables JSON,
PRIMARY KEY (db)
)
将第一个主键放在外面,将第二个主键放在json中,并将第二个主键值设为auto_incr_sequence。
INSERT INTO `DB_TABLE_XREF`
(`db`, `tables`)
VALUES
('account_db', '{"user_info": 1, "seq" : 1}')
ON DUPLICATE KEY UPDATE `tables` =
JSON_SET(`tables`,
'$."user_info"',
IFNULL(`tables` -> '$."user_info"', `tables` -> '$."seq"' + 1),
'$."seq"',
IFNULL(`tables` -> '$."user_info"', `tables` -> '$."seq"' + 1)
);
输出如下
account_db {"user_info" : 1, "user_details" : 2, "seq" : 2}
product_db {"product1" : 1, "product2" : 2, "product3" : 3, "seq" : 3}
如果您的辅助密钥很大,并且害怕使用json,那么我建议使用存储过程,检查MAX(secondary_column)以及下面的锁定。
SELECT table_id INTO t_id FROM DB_TABLE_XREF WHERE database = db_name AND table = table_name;
IF t_id = 0 THEN
SELECT GET_LOCK(db_name, 10) INTO acq_lock;
-- CALL debug_msg(TRUE, "Acquiring lock");
IF acq_lock = 1 THEN
SELECT table_id INTO t_id FROM DB_TABLE_XREF WHERE database_id = db_name AND table = table_name;
-- double check lock
IF t_id = 0 THEN
SELECT IFNULL((SELECT MAX(table_id) FROM (SELECT table_id FROM DB_TABLE_XREF WHERE database = db_name) AS something), 0) + 1 into t_id;
INSERT INTO DB_TABLE_XREF VALUES (db_name, table_name, t_id);
END IF;
ELSE
-- CALL debug_msg(TRUE, "Failed to acquire lock");
END IF;
COMMIT;