mysql具有自动递增功能的两列主键

mysql具有自动递增功能的两列主键,mysql,Mysql,我有多个具有相同结构的数据库,其中数据有时会跨多个数据库进行复制。为了保持数据完整性,我使用两列作为主键。一个是数据库id,它链接到包含每个数据库信息的表。另一个是表键。它不是唯一的,因为它可能有多行,并且该值相同,但在database_id列中的值不同 我计划将这两个列合并成一个主键。但是,我还希望将表键设置为自动递增-但基于数据库的id列 例如,有了这些数据: table_id database_id other_columns 1 1 2 1

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

我计划将这两个列合并成一个主键。但是,我还希望将表键设置为自动递增-但基于数据库的id列

例如,有了这些数据:

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


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

如果您使用的是myisam

对于MyISAM和BDB表,您可以 在辅助设备上指定自动增量 多列索引中的列。在里面 在这种情况下,生成的 自动增量列的计算公式为 最大值(自动增量列)+1,其中 前缀=给定的前缀。这是有用的 当您要将数据放入已排序的 小组

例如:

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)

当使用innodb时,这里有一种方法,由于聚集复合索引,它的性能也会非常好-仅适用于innodb


您可以使两列主键
唯一
和自动递增键
主键

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外部,第二个主键放在json内部,并将第二个主键值设置为auto_incr_序列

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;

对不起,我应该指定我正在使用MyISAM。不管怎样,我还是投票给你了。自述@f00,这种方法需要应用程序级同步,以防多个线程同时更新同一个数据库,对吗?警告!这会导致复制问题,请参阅
插入到具有复合主键的表中,该主键包含一个自动增量列(该列不是该复合键的第一列),这是不安全的[…]
@demonlay335,该列似乎不正确-这在使用InnoDB的MySQL 5.5中不起作用。所以,没有事务的二次自动增量…@brilliandodd,我在文档中看到了。我确实遇到了在我的生产服务器(MySQL 5.5.34)上无法工作的问题。最后,我让我的应用程序使用与数据库内部相同的查询来完成这项工作(
MAX(auto_increment_column)+1,其中prefix=giveprefix
)。@demonlay335除非将主键的顺序改为(id,grp)而不是(grp,id),否则它在InnoDB中不起作用,很明显,自动增量列必须先运行。据我所知,该功能永远不会被放入InnoDB中。我没有在8.0中看到它。建议你在bugs.mysql.com上投票。我不知道这与这个问题有什么关系。@eatonemerich用户想要的只是基于两列的序列开始。这在Innodb引擎中是不可能的。因此,我们建议使用json来实现。那么这些sql查询会在InnoDB上运行吗?@eatonemerich是的。
account_db    {"user_info" : 1, "user_details" : 2, "seq" : 2}
product_db    {"product1" : 1, "product2" : 2,  "product3" : 3, "seq" : 3}
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;