Indice di più colonne quando si utilizza l'estensione ORM dichiarativa di sqlalchemy


95

Secondo la documentazione e i commenti nella sqlalchemy.Columnclasse, dovremmo usare la classe sqlalchemy.schema.Indexper specificare un indice che contiene più colonne.

Tuttavia, l'esempio mostra come farlo utilizzando direttamente l'oggetto Table in questo modo:

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)

Come dovremmo farlo se usiamo l'estensione dichiarativa ORM?

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

Vorrei un indice sulle colonne "a" e "b".


1
La domanda è un po 'poco chiara se si desidera più indici o un singolo indice su più colonne (ed era più confuso prima che lo modificassi - in origine chiedeva deliziosamente "un indice che contenga più indici multipli" ). Ma non importa, immagino, poiché la risposta di zzzeek affronta entrambi i casi.
Mark Amery

Risposte:


138

quelli sono solo Columnoggetti, index = True flag funziona normalmente:

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

se desideri un indice composto, di nuovo Tableè presente qui come al solito non devi dichiararlo, tutto funziona allo stesso modo (assicurati di essere sulla versione 0.6 o 0.7 recente per il wrapper Aa dichiarativo da interpretare come un Columndopo che la dichiarazione di classe è stata completata):

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)

In 0.7 Indexpuò essere anche negli Tableargomenti, che con dichiarativo è tramite __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"), )

1
Grazie, ho aggiornato alla 0.7 e usando table_args funziona bene
yorjo

6
Cosa succede se hai un dizionario per table_args come lo faccio attualmente? table_args = {'mysql_engine': 'InnoDB'}
Nick Holden


7
Quindi immagino di poter fare table_args = (Index ('my_index', "a", "b"), {'mysql_engine': 'InnoDB'})
Nick Holden

1
@RyanChou docs.sqlalchemy.org/en/latest/orm/extensions/declarative/… "Gli argomenti delle parole chiave possono essere specificati con il modulo sopra specificando l'ultimo argomento come dizionario"
zzzeek

13

Per completare la risposta di @ zzzeek .

Se ti piace aggiungere un indice composto con DESC e utilizzare il metodo dichiarativo ORM puoi fare come segue.

Inoltre, stavo lottando con la documentazione degli indici funzionali di SQSAlchemy, cercando di capire come sostituire mytable.c.somecol.

from sqlalchemy import Index

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

Possiamo semplicemente usare la proprietà del modello e chiamarla .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),
    )

Se usi Alembic, sto usando Flask-Migrate, genera qualcosa come:

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 ###

Infine dovresti avere la seguente tabella e gli indici nel tuo database 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)
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.