列的唯一顺序编号

时间:2013-05-02 20:33:13

标签: sqlalchemy

我需要创建序列,但在通用情况下不使用Sequence类。

USN = Column(Integer, nullable = False, default=nextusn, server_onupdate=nextusn)

,此函数nextusn需要在模型中生成func.max(table.USN)行的值。

我尝试使用此

class nextusn(expression.FunctionElement):
    type = Numeric()
    name = 'nextusn'

@compiles(nextusn)
def default_nextusn(element, compiler, **kw):
    return select(func.max(element.table.c.USN)).first()[0] + 1

但是在此上下文元素中不知道element.table。有解决方法吗?

1 个答案:

答案 0 :(得分:0)

由于以下原因,这有点棘手:

  1. 如果表为空,SELECT MAX()将返回NULL;您应该使用COALESCE来生成默认的“种子”值。见下文。

  2. 使用SELECT MAX插入行的整个方法对于并发使用来说完全不安全 - 所以你需要确保一次只有一个INSERT语句在表上调用,否则你可能会违反约束(你应该肯定在这个专栏上有某种约束。)

  3. 从SQLAlchemy的角度来看,
  4. 需要您的自定义元素才能知道实际的Column元素。我们可以通过在事实之后将“nextusn()”函数分配给Column来实现这一点,或者在下面我将使用事件显示更复杂的方法。

  5. 我不明白你的目的是什么“server_onupdate = nextusn”。 SQLAlchemy中的“server_onupdate”实际上并没有为您运行任何SQL,如果您创建了一个触发器,这是一个占位符;而且“SELECT MAX(id)FROM table”的东西是一个INSERT模式,我不确定你是否意味着在UPDATE发生任何事情。

  6. @compiles扩展需要返回一个字符串,通过compiler.process()运行select()。见下文。

  7. 示例:

    from sqlalchemy import Column, Integer, create_engine, select, func, String
    from sqlalchemy.ext.declarative import declarative_base
    from sqlalchemy.sql.expression import ColumnElement
    from sqlalchemy.schema import ColumnDefault
    from sqlalchemy.ext.compiler import compiles
    from sqlalchemy import event
    
    class nextusn_default(ColumnDefault):
        "Container for a nextusn() element."
        def __init__(self):
            super(nextusn_default, self).__init__(None)
    
    @event.listens_for(nextusn_default, "after_parent_attach")
    def set_nextusn_parent(default_element, parent_column):
        """Listen for when nextusn_default() is associated with a Column,
        assign a nextusn().
    
        """
        assert isinstance(parent_column, Column)
        default_element.arg = nextusn(parent_column)
    
    
    class nextusn(ColumnElement):
        """Represent "SELECT MAX(col) + 1 FROM TABLE".
        """
        def __init__(self, column):
            self.column = column
    
    @compiles(nextusn)
    def compile_nextusn(element, compiler, **kw):
        return compiler.process(
                    select([
                        func.coalesce(func.max(element.column), 0) + 1
                    ]).as_scalar()
                )
    
    Base = declarative_base()
    
    class A(Base):
        __tablename__ = 'a'
    
        id = Column(Integer, default=nextusn_default(), primary_key=True)
        data = Column(String)
    
    e = create_engine("sqlite://", echo=True)
    
    Base.metadata.create_all(e)
    
    # will normally pre-execute the default so that we know the PK value
    # result.inserted_primary_key will be available
    e.execute(A.__table__.insert(), data='single row')
    
    # will run the default expression inline within the INSERT
    e.execute(A.__table__.insert(), [{"data": "multirow1"}, {"data": "multirow2"}])
    
    # will also run the default expression inline within the INSERT,
    # result.inserted_primary_key will not be available
    e.execute(A.__table__.insert(inline=True), data='single inline row')