【问题标题】:Alternative to Max(ID) in complex primary key复杂主键中 Max(ID) 的替代方案
【发布时间】:2010-11-08 23:50:06
【问题描述】:

我正在制作一个发票系统,支持多个子公司,每个子公司都有自己的一组发票号码,因此我有一个主键为 (Subsidiary, InvoiceNo) 的表

我不能使用 MySQL 自动增量字段,因为它会不断地为所有子公司增加相同的计数。

我不想为每个子公司制作单独的表格,因为会根据需要添加新的子公司...

我目前正在使用“Select Max (ID) Where Subsidiary = X”,从我的表中并根据此添加发票。

我正在使用nHibernate,并且Invoice插入在InvoiceItem插入之前,因此如果Invoice插入失败,InvoiceItem将不会被执行。但相反,我会捕获异常,重新检索 Max(ID) 并重试。

这种方法有什么问题?如果有的话,还有什么替代方案?

询问的原因是因为我阅读了有关此问题的答案之一:Nhibernate Criteria: 'select max(id)'

【问题讨论】:

    标签: c# database nhibernate primary-key


    【解决方案1】:

    在生成主键时这是一个非常糟糕的主意。我的建议如下:

    • 不要赋予主键业务意义(合成键);

    • 使用辅助机制生成发票编号。

    这将使您的生活更轻松。然后,生成发票编号的机制可以例如是一个看起来像这样的表:

    • 子公司;
    • 下一个发票号码。

    这会将内部编号与数据库的工作方式分开。

    通过这种机制,您将能够再次使用自动递增字段,甚至更好地使用 GUID。

    一些阅读材料的链接:

    http://fabiomaulo.blogspot.com/2008/12/identity-never-ending-story.html http://nhforge.org/blogs/nhibernate/archive/2009/02/09/nh2-1-0-new-generators.aspx

    【讨论】:

    • 在那张桌子上我仍然会遇到锁定问题。虽然我将来可能会实施以简化 PreLoadEvent 安全检查的实施。
    • 您的锁定问题将得到严重缓解,因为您只锁定了一条记录。要获得一致的 MAX(ID),理论上您必须锁定整个表,因为您要锁定的是您希望 SELECT MAX(ID) WHERE Subsidiary = ? 更改。有显着差异。此外,锁定附属表,您正在做一些“奇怪”的事情,因为附属表与发票表无关。这只是一种锁定策略,但您没有对数据做任何事情。
    【解决方案2】:

    正如您所说,这种方法的问题是多个会话可能会尝试插入相同的发票 ID。你得到一个独特的约束违反,必须再试一次,这也可能失败,等等。

    我通过在创建新发票期间锁定子公司来解决此类问题。但是,不要锁定表,(a) 如果您使用 InnoDB,则默认情况下lock table 命令会出现commit the transaction 的问题。 (b) 没有理由不应该同时添加两个不同子公司的发票,因为它们具有不同的独立发票编号。

    在你的情况下我会做的是:

    • 打开一个事务并确保您的表是 InnoDB。
    • 使用SELECT .. FOR UPDATE 命令锁定子公司。这可以在 NHibernate 中使用 LockMode.UPGRADE 来完成。
    • 使用 max(..) 函数查找最大 id 并进行插入
    • 提交事务

    这会序列化一个子公司的所有发票插入(即只有一个会话可以一次执行此类插入,任何第二次尝试都将等到第一次完成或回滚)但这就是您想要的。您不希望发票编号中出现漏洞(例如,如果您插入发票 id 3485,然后失败,则有发票 3484 和 3486,但没有 3485)。

    【讨论】:

    • 这看起来正是我正在寻找的东西,你能否再清理一下 lockign 过程。我是否需要从 subinstance = x 的发票中选择 * 进行更新,然后在同一会话上进行另一个查询以获取最大 ID?
    • 您需要一张表subsiduary,其中每个子公司有一行。这将是您要锁定的那一行,以表明子公司正在进行独占操作。 START TRANSACTION,然后是SELECT id FROM subsiduary WHERE subsiduary_id=? FOR UPDATE,然后是SELECT MAX(id) FROM invoice ...INSERT...COMMIT
    • 所以当你锁定附属表中的附属行时,它不会让任何新记录被插入,这行作为外键约束?对吗?
    • 不完全;如果您锁定附属表中的行,那么在您提交或回滚之前,没有其他人可以锁定该特定行。然后你做什么(例如更新该行,做其他事情,选择最大发票ID然后插入新发票)取决于你。
    猜你喜欢
    • 1970-01-01
    • 2020-08-30
    • 1970-01-01
    • 2014-07-31
    • 2016-07-22
    • 2019-10-07
    • 1970-01-01
    • 2011-08-22
    • 1970-01-01
    相关资源
    最近更新 更多