【问题标题】:How to insert a row with autoincrement id in a multi-primary-key table?如何在多主键表中插入具有自动增量 id 的行?
【发布时间】:2010-11-25 05:51:25
【问题描述】:

我正在编写一个 turbogears2 应用程序。我有一张这样的桌子:

class Order(DeclarativeBase):
    __tablename__ = 'order'

    # id of order
    id = Column(Integer, autoincrement=True, primary_key=True)

    # buyer's id
    buyer_id = Column(Integer, ForeignKey('user.user_id',
        onupdate="CASCADE", ondelete="CASCADE"), primary_key=True)

我想在此表中插入一个新行,但出现“字段 'order_id' 没有默认值”错误。看来我必须手动设置订单的id,因为我有两个主键。我的问题是,如何插入自动生成新 ID 的行?

如果我手动生成 id,我会遇到一些问题。例如:

maxId = DBSession.query(func.max(Order)).one()[0]
newOrder = Order(id=maxId + 1, buyer_id=xxx)
DBSession.add(newOrder)

以这种方式添加新订单似乎没问题,但是,如果两个请求几乎同时运行这些代码,我们就会遇到问题。

如果有请求 a 和 b 按以下顺序运行此代码:

a.maxId = DBSession.query(func.max(Order)).one()[0]
b.maxId = DBSession.query(func.max(Order)).one()[0]
b.newOrder = Order(id=maxId + 1, buyer_id=xxx)
b.DBSession.add(newOrder)
a.newOrder = Order(id=maxId + 1, buyer_id=xxx)
a.DBSession.add(newOrder)

那么请求a可能会失败,因为表中已经有一个id相同的订单。我可以捕获异常并重试。但我想知道,有没有更好的办法?

有时候,id不是简单的整数,我们可能需要这样的订单id:

2009090133 标准 2009-09-01 第 33 次订购

在这些情况下,自动增量不可用。所以我别无选择,为订单手动分配 id。所以我的另一个问题是,有没有比捕获异常并重试插入带有 id 的行更好的方法。

【问题讨论】:

    标签: python sql database sqlalchemy


    【解决方案1】:

    您应该在列定义上使用默认值

    id = Column(Integer, default = sqlexpression)
    

    其中 sqlexpression 可以是 sql 表达式。这是documentation。对于自动增量,您应该使用 sql 表达式 coalesce(select max(order.id) from order,0) + 1。为方便起见,您可以导入 sqlalchemy.sql.text 以便 id 列看起来像

    id = Column(Integer, default = text("coalesce(select max(order.id) from order,0) + 1"))
    

    【讨论】:

      【解决方案2】:

      如果您希望每个买家的订单有序列号,那么您必须对插入一位买家的交易进行序列化。您可以通过获取买方行的排他锁来做到这一点:

      sess.query(Buyer.id).with_lockmode('update').get(xxx)
      order_id = sess.query(func.max(Order.id)+1).filter_by(buyer_id=xxx).scalar() or 1
      sess.add(Order(id=order_id, buyer_id=xxx))
      

      当两笔交易尝试同时为一个买家插入订单时使用此模式,其中一笔交易将在第一行阻塞,直到另一笔交易完成或失败。

      【讨论】:

        猜你喜欢
        • 2014-06-05
        • 1970-01-01
        • 2012-09-11
        • 2012-02-22
        • 1970-01-01
        • 2019-06-23
        相关资源
        最近更新 更多