使用 sqlalchemy 的声明性 ORM 扩展时的多列索引

Posted

技术标签:

【中文标题】使用 sqlalchemy 的声明性 ORM 扩展时的多列索引【英文标题】:Multiple columns index when using the declarative ORM extension of sqlalchemy 【发布时间】:2011-10-01 09:25:39 【问题描述】:

根据the documentation和sqlalchemy.Column类中的cmets,我们应该使用sqlalchemy.schema.Index类来指定一个包含多列的索引。

但是,该示例显示了如何通过直接使用 Table 对象来做到这一点,如下所示:

meta = MetaData()
mytable = Table('mytable', meta,
    # an indexed column, with index "ix_mytable_col1"
    Column('col1', Integer, index=True),

    # a uniquely indexed column with index "ix_mytable_col2"
    Column('col2', Integer, index=True, unique=True),

    Column('col3', Integer),
    Column('col4', Integer),

    Column('col5', Integer),
    Column('col6', Integer),
    )

# place an index on col3, col4
Index('idx_col34', mytable.c.col3, mytable.c.col4)

如果我们使用声明式ORM扩展应该怎么做?

class A(Base):
    __tablename__ = 'table_A'
    id = Column(Integer, , primary_key=True)
    a = Column(String(32))
    b = Column(String(32))

我想要列“a”和“b”的索引。

【问题讨论】:

这个问题有点不清楚你是否想要多个索引或多个列上的单个索引(并且在我编辑它之前更加困惑 - 最初它很高兴地要求 “包含多重多重索引”)。但没关系,我想,因为 zzzeek 的回答解决了这两种情况。 问题没有说明联合索引是否应该是唯一的 【参考方案1】:

完成@zzzeek 的answer。

如果你想用 DESC 添加复合索引并使用 ORM 声明式方法,你可以这样做。

此外,我在 SQLAlchemy 的 Functional Indexes 文档中苦苦挣扎,试图找出如何替换 mytable.c.somecol

from sqlalchemy import Index

Index('someindex', mytable.c.somecol.desc())

我们可以只使用模型属性并在其上调用.desc()

from flask_sqlalchemy import SQLAlchemy

db = SQLAlchemy()

class GpsReport(db.Model):
    __tablename__ = 'gps_report'

    id = db.Column(db.Integer, db.Sequence('gps_report_id_seq'), nullable=False, autoincrement=True, server_default=db.text("nextval('gps_report_id_seq'::regclass)"))

    timestamp = db.Column(db.DateTime, nullable=False, primary_key=True)

    device_id = db.Column(db.Integer, db.ForeignKey('device.id'), primary_key=True, autoincrement=False)
    device = db.relationship("Device", back_populates="gps_reports")


    # Indexes

    __table_args__ = (
        db.Index('gps_report_timestamp_device_id_idx', timestamp.desc(), device_id),
    )

如果你使用 Alembic,我使用的是 Flask-Migrate,它会生成如下内容:

from alembic import op  
import sqlalchemy as sa
# Added manually this import
from sqlalchemy.schema import Sequence, CreateSequence


def upgrade():
    # ### commands auto generated by Alembic - please adjust! ###
    # Manually added the Sequence creation
    op.execute(CreateSequence(Sequence('gps_report_id_seq')))

    op.create_table('gps_report',
    sa.Column('id', sa.Integer(), server_default=sa.text("nextval('gps_report_id_seq'::regclass)"), nullable=False),
    sa.Column('timestamp', sa.DateTime(), nullable=False))
    sa.Column('device_id', sa.Integer(), autoincrement=False, nullable=False),
    op.create_index('gps_report_timestamp_device_id_idx', 'gps_report', [sa.text('timestamp DESC'), 'device_id'], unique=False)


def downgrade():
    # ### commands auto generated by Alembic - please adjust! ###
    op.drop_index('gps_report_timestamp_device_id_idx', table_name='gps_report')
    op.drop_table('gps_report')

    # Manually added the Sequence removal
    op.execute(sa.schema.DropSequence(sa.Sequence('gps_report_id_seq'))) 
    # ### end Alembic commands ###

最后,您的 PostgreSQL 数据库中应该有以下表和索引:

psql> \d gps_report;
                                           Table "public.gps_report"
     Column      |            Type             | Collation | Nullable |                Default                 
-----------------+-----------------------------+-----------+----------+----------------------------------------
 id              | integer                     |           | not null | nextval('gps_report_id_seq'::regclass)
 timestamp       | timestamp without time zone |           | not null | 
 device_id       | integer                     |           | not null | 
Indexes:
    "gps_report_pkey" PRIMARY KEY, btree ("timestamp", device_id)
    "gps_report_timestamp_device_id_idx" btree ("timestamp" DESC, device_id)
Foreign-key constraints:
    "gps_report_device_id_fkey" FOREIGN KEY (device_id) REFERENCES device(id)

【讨论】:

我无法使用Index函数o.O中的类属性【参考方案2】:

那些只是 Column 对象,index=True 标志正常工作:

class A(Base):
    __tablename__ = 'table_A'
    id = Column(Integer, primary_key=True)
    a = Column(String(32), index=True)
    b = Column(String(32), index=True)

如果你想要一个复合索引,Table 像往常一样出现在这里,你不必声明它,一切都一样(确保你在最近的 0.6 或 0.7 上使用声明性 Aa包装器在类声明完成后被解释为Column):

class A(Base):
    __tablename__ = 'table_A'
    id = Column(Integer, primary_key=True)
    a = Column(String(32))
    b = Column(String(32))

Index('my_index', A.a, A.b)

在 0.7 中,Index 也可以在 Table 参数中,声明式通过 __table_args__

class A(Base):
    __tablename__ = 'table_A'
    id = Column(Integer, primary_key=True)
    a = Column(String(32))
    b = Column(String(32))
    __table_args__ = (Index('my_index', "a", "b"), )

【讨论】:

谢谢,我更新到 0.7 并且使用 table_args 工作正常 如果你有一个 table_args 字典,就像我现在做的那样? table_args = 'mysql_engine':'InnoDB' @Nick sqlalchemy.org/docs/orm/extensions/… 所以我想我可以做到 table_args = (Index('my_index', "a", "b"),'mysql_engine':'InnoDB') @RyanChou docs.sqlalchemy.org/en/latest/orm/extensions/declarative/… "通过将最后一个参数指定为字典,可以使用上述形式指定关键字参数"

以上是关于使用 sqlalchemy 的声明性 ORM 扩展时的多列索引的主要内容,如果未能解决你的问题,请参考以下文章

为啥 SQLAlchemy Postgres ORM 需要 __init__(self) 作为声明性基础?

如何使用额外的方法扩展 SQLAlchemy 绑定声明性模型?

如何在db更新后获取SQLAlchemy ORM对象的先前状态?

如何在 SQLAlchemy ORM 上实现对同一属性的自引用多对多关系?

如何使用 SQLAlchemy 声明性语法指定关系?

使用 SQLAlchemy 的声明性语法时访问表实例的最佳方式