【问题标题】:2 auto_increment fields where 1 resets2 个 auto_increment 字段,其中 1 个重置
【发布时间】:2013-10-22 09:18:42
【问题描述】:

我将编写几个 MySQL 表来处理发票。

我的计划是将其分为 3 个主要表格:

create table invoice
(
  id auto_increment,
  client (foreign key),
  created (date),
  *etc*...
)

create table products
(
  id auto_increment
  *product info*...
)

create table invoice_products
(
  invoice_id (references invoice.id)
  row (resetting auto_increment)  <--THIS!!!
  product_id(references products.id)
  product_quantity INT
  primary key (invoice_id,row)
)

难题在于,当创建新发票时,invoice.id 是 auto_incremented,这是它应该的。我不希望 invoice_products.row 从每张新发票的 1 开始。 因此,对于每张新发票,行 auto_increment 将从 1 开始,但如果将新行添加到现有发票 ID,则行 ID 将从中断处继续。

关于如何实现这一点的任何建议?

(希望简短版的代码足以让你理解困境)

提前感谢您的任何建议!

编辑:澄清:数据库中的所有表都是 InnoDB(因为大量使用外键约束)

【问题讨论】:

  • 我认为您正在尝试混合 数据库设计演示文稿。只需在检索发票行后添加行号,不是吗?
  • MyISAM 引擎可以做到这一点 - 完全符合您的设计 (primary key (invoice_id,row))。如果您使用 InnoDB,则必须在代码中进行(SQL 或应用程序)。
  • @GuillaumePoussel:唯一的困境是没有办法检查一致性。并且没有可靠的方法来引用发票上的特定行。一张发票可能有多行包含相同的产品,甚至是相同的数量。
  • @ypercube:我选择 InnoDB 的原因是外键支持。
  • 我并不是因为这个而暗示你去 MyISAM。 InnoDB 是目前最好的选择 - 正是因为完整性/事务/外键支持,并且因为它正在积极开发。

标签: mysql database-design innodb auto-increment


【解决方案1】:

您所要做的就是将您的表更改为使用 MyISAM 引擎。但请注意,MyISAM 不支持事务和外键。

无论如何,这是一个如何工作的示例(引用manual):

对于 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;

返回:

+--------+----+---------+
| grp    | id | name    |
+--------+----+---------+
| fish   |  1 | lax     |
| mammal |  1 | dog     |
| mammal |  2 | cat     |
| mammal |  3 | whale   |
| bird   |  1 | penguin |
| bird   |  2 | ostrich |
+--------+----+---------+

在这种情况下(当 AUTO_INCREMENT 列是多列索引的一部分时),如果您删除任何组中具有最大 AUTO_INCREMENT 值的行,则会重用 AUTO_INCREMENT 值。即使是 MyISAM 表也会发生这种情况,因为 AUTO_INCREMENT 值通常不会被重用。

如果 AUTO_INCREMENT 列是多个索引的一部分,MySQL 将使用以 AUTO_INCREMENT 列开头的索引(如果有的话)生成序列值。例如,如果动物表包含索引 PRIMARY KEY (grp, id) 和 INDEX (id),MySQL 将忽略 PRIMARY KEY 来生成序列值。结果,该表将包含单个序列,而不是每个 grp 值的序列。

如您所见,您的主键和 auto_increment 设置已经正确。只需更改为 MyISAM。


如果不想使用MyISAM,也可以边选择边计算。

SELECT
ip.*,
@row := IF(@prev_inv != invoice_id, 1, @row + 1) AS `row`,
@prev_inv := invoice_id
FROM invoice_products ip
, (SELECT @row:=1, @prev_inv:=NULL) vars
ORDER BY invoice_id

第三种可能性当然是在数据库之外进行计算。我会留给你的:)

【讨论】:

  • 谢谢!这就是我一直在寻找的。我唯一担心的是数据库中的所有其他表都是 innodb,因为可能使用外键约束。由于该解决方案似乎在 innodb 中不起作用,我可能会通过 PHP 客户端手动插入行号。我将把它留给 MySQL 检查 invoice_id 和 row_id 是否唯一-> UNIQUE(invoice_id,row_id) 以防止在一张发票上多次输入同一行 no。会给你一个赞成票,但没有代表:)
【解决方案2】:

为此,您不能使用 auto_increment 列。您应该在每次插入之前查询实际发票的 max(row) + 1。

【讨论】:

  • 这不仅在性能方面不是最理想的,而且您还必须处理并发问题。如果在检索 max(row) 和您的插入之间还有另一个插入怎么办?此外,这根本不应该在数据库中完成。
  • 在 sql server 中有表提示。我不知道这对mysql是否有帮助。但在其他 dbms 中,有一个非自动增量 id 是很常见的。
猜你喜欢
  • 2015-07-24
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-09-30
  • 1970-01-01
  • 1970-01-01
  • 2014-12-31
  • 1970-01-01
相关资源
最近更新 更多