sqlalchemy, trasformando un elenco di Id di un elenco di oggetti

Ho sequenza di Id voglio recuperare. E ‘ semplice:

session.query(Record).filter(Record.id.in_(seq)).all()

C’è un modo migliore per farlo?

  • Cosa non ti piace? Non lavoro? Sembra come dovrebbe.
  • Funziona, io sono solo chiedendo se c’era qualche modo più bello per farlo.
  • Che cosa si intende per “più bello”? Cosa non ti piace di questo?
  • Essere attenti che se seq ottiene abbastanza a lungo, si può ottenere un ‘troppe variabili SQL’ eccezione, poiché la clausola è parametrizzato e ci sono troppi parametri.
InformationsquelleAutor Cheery | 2009-01-14

 

5 Replies
  1. 20

    Il codice è absolutety bene.

    IN è come un mazzo di X=Y aderito con OR ed è molto veloce in contemporanea database.

    Tuttavia, se il vostro elenco di Id è lungo, si potrebbe rendere la query un po ‘ più efficiente passando un sub-query che restituisca l’elenco di Id.

  2. 6

    Il codice è completamente bene. Tuttavia, qualcuno mi chiede qualche sistema di copertura tra i due approcci di fare un grosso IN e utilizzando get() per i singoli Id.

    Se qualcuno è veramente cercando di evitare di SELEZIONARE, quindi il modo migliore per farlo è quello di impostare gli oggetti in memoria prima del tempo. Ad esempio, si sta lavorando su un grande tavolo di elementi. Suddividere il lavoro in blocchi, ad esempio, ordinare il set completo di lavoro da parte di chiave primaria, o per intervallo di date, a prescindere, quindi caricare il tutto per quel pezzo localmente nella cache:

     all_ids = [<huge list of ids>]
    
     all_ids.sort()
     while all_ids:
         chunk = all_ids[0:1000]
    
         # bonus exercise!  Throw each chunk into a multiprocessing.pool()!
         all_ids = all_ids[1000:]
    
         my_cache = dict(
               Session.query(Record.id, Record).filter(
                     Record.id.between(chunk[0], chunk[-1]))
         )
    
         for id_ in chunk:
             my_obj = my_cache[id_]
             <work on my_obj>

    Che il mondo reale caso d’uso.

    Ma anche di illustrare alcuni SQLAlchemy API, possiamo fare una funzione che fa il record non abbiamo e di un locale per ottenere per quelli di noi. Qui è che:

    from sqlalchemy import inspect
    
    
    def get_all(session, cls, seq):
        mapper = inspect(cls)
        lookup = set()
        for ident in seq:
            key = mapper.identity_key_from_primary_key((ident, ))
            if key in session.identity_map:
                yield session.identity_map[key]
            else:
                lookup.add(ident)
        if lookup:
            for obj in session.query(cls).filter(cls.id.in_(lookup)):
                yield obj

    Qui è una dimostrazione:

    from sqlalchemy import Column, Integer, create_engine, String
    from sqlalchemy.orm import Session
    from sqlalchemy.ext.declarative import declarative_base
    import random
    
    Base = declarative_base()
    
    
    class A(Base):
        __tablename__ = 'a'
        id = Column(Integer, primary_key=True)
        data = Column(String)
    
    e = create_engine("sqlite://", echo=True)
    Base.metadata.create_all(e)
    
    ids = range(1, 50)
    
    s = Session(e)
    s.add_all([A(id=i, data='a%d' % i) for i in ids])
    s.commit()
    s.close()
    
    already_loaded = s.query(A).filter(A.id.in_(random.sample(ids, 10))).all()
    
    assert len(s.identity_map) == 10
    
    to_load = set(random.sample(ids, 25))
    all_ = list(get_all(s, A, to_load))
    
    assert set(x.id for x in all_) == to_load
  3. 4

    Se si utilizza composito primario tasti, è possibile utilizzare tuple_, come in

    from sqlalchemy import tuple_
    session.query(Record).filter(tuple_(Record.id1, Record.id2).in_(seq)).all()

    Di notare che questa funzione non è disponibile su SQLite (vedi doc).

    • Grazie era proprio quello che stavo cercando !
  4. 1

    Vi consiglio di dare un’occhiata a SQL produce. Si può solo stampare str(query) per vederli.

    Io non sono a conoscenza di un modo ideale di fare con SQL standard.

  5. 1

    C’è un altro modo; Se è ragionevole aspettarsi che gli oggetti in questione sono già caricati nella sessione; li hai accesso prima nella stessa transazione, è possibile invece fare:

    map(session.query(Record).get, seq)

    Nel caso In cui tali oggetti sono già presenti, questo sarà molto più veloce, dal momento che non ci sarà alcuna query per recuperare gli oggetti; d’altra parte, se più di un piccolo numero di questi oggetti sono non caricato, sarà molto, molto più lento, poiché causerebbe una interrogazione al mancanti esempio, invece di una singola query per tutti gli oggetti.

    Questo può essere utile quando si sta facendo joinedload() query prima di raggiungere il punto di cui sopra, in modo che si può essere sicuri che essi sono stati caricati già. In generale, si consiglia di utilizzare la soluzione in questione, per impostazione predefinita, e solo esplorare questa soluzione quando avete visto che si sta eseguendo la query per gli stessi oggetti più e più volte.

Lascia un commento