使用sqlalchemy的声明式ORM扩展时,如何创建多列索引?

124
根据文档sqlalchemy.Column类中的注释,我们应该使用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"创建索引。


1
这个问题有点不太清楚,不确定你是想要在多个列上建立单个索引还是在多个列上建立多个索引(在我编辑之前更加混乱 - 最初它令人愉快地要求“包含多个多个索引的索引”)。但无论如何,zzzeek的答案都可以解决这两种情况。 - Mark Amery
该问题并未指明联合索引是否应该是唯一的。 - Pynchia
2个回答

177

这些只是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版本,以使得在类声明完成之后将A.a包装为一个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"), )

2
谢谢,我已经更新到0.7版本,并且使用__table_args__也很好用。 - yorjo
8
如果你像我现在一样拥有一个像table_args这样的字典,会发生什么?table_args = {'mysql_engine':'InnoDB'} - Nick Holden
9
我猜我可以这样做 table_args = (Index('my_index', "a", "b"),{'mysql_engine':'InnoDB'}) - Nick Holden
我正在使用Flask扩展和sqlalchemy 0.8,但第三个示例导致了错误:AttributeError:'Function'对象和'Comparator'对象都没有'key'属性。 - Ellochka Cannibal
4
“可以通过指定最后一个参数为字典的形式来使用上述关键字参数。” 这句话的意思是:在使用上述形式时,可以通过将最后一个参数指定为字典来指定关键字参数。 - zzzeek
显示剩余8条评论

25

为了补充@zzzeek的答案

如果你想使用ORM声明方法添加一个包含DESC的复合索引,你可以按照以下步骤进行。

此外,我一直在苦苦探索SQLAlchemy的功能性索引文档,试图找出如何替换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)

2
我无法在Index函数内部使用类属性 o.O - rickerp

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接