mysql 两列主键自动递增

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/5416548/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-31 19:15:59  来源:igfitidea点击:

mysql two column primary key with auto-increment

mysql

提问by SystemicPlural

I have multiple databases with the same structure in which data is sometimes copied across. In order to maintain data integrity I am using two columns as the primary key. One is a database id, which links to a table with info about each database. The other is a table key. It is not unique because it may have multiple rows with this value being the same, but different values in the database_id column.

我有多个具有相同结构的数据库,有时会在其中复制数据。为了保持数据完整性,我使用两列作为主键。一个是数据库 ID,它链接到一个表,其中包含有关每个数据库的信息。另一个是表键。它不是唯一的,因为它可能有多个行,此值相同,但 database_id 列中的值不同。

I am planning on making the two columns into a joint primary key. However I also want to set the table key to auto increment - but based on the database_id column.

我打算将这两列变成一个联合主键。但是,我也想将表键设置为自动递增 - 但基于 database_id 列。

EG, With this data:

EG,有了这个数据:

table_id   database_id     other_columns
1          1
2          1
3          1
1          2
2          2

If I am adding data that includes the dabase_id of 1 then I want table_id to be automatically set to 4. If the dabase_id is entered as 2 then I want table_id to be automatically set to 3. etc.

如果我添加的数据包含 1 的 dabase_id,那么我希望 table_id 自动设置为 4。如果 dabase_id 输入为 2,那么我希望 table_id 自动设置为 3。等等。

What is the best way of achieving this in MySql.

在 MySql 中实现这一目标的最佳方法是什么。

回答by DTing

if you are using myisam

如果您使用的是 myisam

http://dev.mysql.com/doc/refman/5.0/en/example-auto-increment.html

http://dev.mysql.com/doc/refman/5.0/en/example-auto-increment.html

For MyISAM and BDB tables you can specify AUTO_INCREMENT on a secondary column in a multiple-column index. In this case, the generated value for the AUTO_INCREMENT column is calculated as MAX(auto_increment_column) + 1 WHERE prefix=given-prefix. This is useful when you want to put data into ordered groups.

对于 MyISAM 和 BDB 表,您可以在多列索引中的辅助列上指定 AUTO_INCREMENT。在这种情况下,AUTO_INCREMENT 列的生成值计算为 MAX(auto_increment_column) + 1 WHERE prefix=given-prefix。当您想将数据放入有序组中时,这很有用。

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 |
+--------+----+---------+

For your example:

对于您的示例:

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)

回答by Jon Black

Here's one approach when using innodb which will also be very performant due to the clustered composite index - only available with innodb...

这是使用 innodb 时的一种方法,由于聚集复合索引,它也将非常高效 - 仅适用于 innodb ...

http://dev.mysql.com/doc/refman/5.0/en/innodb-index-types.html

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;

回答by ElonChan

you can make the two column primary keyuniqueand the auto-incrementkey primary.

您可以制作两列主键unique自增primary

回答by Kanagavelu Sugumar

The solution provided by DTing is excellent and working. But when tried the same in AWS Aurora, it didn't worked and complaining the below error.

DTing 提供的解决方案非常出色且有效。但是当在 AWS Aurora 中尝试相同时,它没有工作并抱怨以下错误。

Error Code: 1075. Incorrect table definition; there can be only one auto column and it must be defined as a key

Hence suggesting json based solution here.

因此在这里建议基于 json 的解决方案。

CREATE TABLE DB_TABLE_XREF (
    db             VARCHAR(36) NOT NULL,
    tables         JSON,
    PRIMARY KEY    (db)
)

Have the first primary key outside, and second primary key inside the json and make second primary key value as auto_incr_sequence.

将第一个主键放在外面,将第二个主键放在 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)
  );

And the output is like below

输出如下

account_db    {"user_info" : 1, "user_details" : 2, "seq" : 2}
product_db    {"product1" : 1, "product2" : 2,  "product3" : 3, "seq" : 3}

If your secondary keys are huge, and afraid of using json, then i would suggest to have stored procedure, to check for MAX(secondary_column) along with lock like below.

如果您的辅助键很大,并且害怕使用 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;