Frage

Ich beginne eine neue Anwendung und Blick auf eine ORM -. Insbesondere SQLAlchemy

Sagen wir, ich habe eine Spalte ‚foo‘ bekam in meiner Datenbank und ich möchte es erhöhen. In gerade SQLite, das ist einfach:

db = sqlite3.connect('mydata.sqlitedb')
cur = db.cursor()
cur.execute('update table stuff set foo = foo + 1')

dachte ich, das SQLAlchemy SQL-Builder-Äquivalent aus:

engine = sqlalchemy.create_engine('sqlite:///mydata.sqlitedb')
md = sqlalchemy.MetaData(engine)
table = sqlalchemy.Table('stuff', md, autoload=True)
upd = table.update(values={table.c.foo:table.c.foo+1})
engine.execute(upd)

Das ist etwas langsamer, aber es gibt nicht viel drin.

Hier ist meine beste Vermutung für einen SQLAlchemy ORM Ansatz:

# snip definition of Stuff class made using declarative_base
# snip creation of session object
for c in session.query(Stuff):
    c.foo = c.foo + 1
session.flush()
session.commit()

Das tut das Richtige, aber es dauert knapp fünfzig mal so lang wie die beiden anderen Ansätze. Ich nehme an, das ist, weil es alle Daten in den Speicher zu bringen, bevor es mit ihm arbeiten können.

Gibt es eine Möglichkeit den effizienten Verwendung von SQL SQLAlchemy ORM zu generieren? Oder unter Verwendung eines beliebigen anderen ORM Python? Oder sollte ich gehen nur zurück, um die SQL von Hand zu schreiben?

War es hilfreich?

Lösung

SQLAlchemy ORM soll mit der SQL-Schicht zusammen verwendet werden, es nicht verbergen. Aber Sie haben ein oder zwei Dinge im Auge zu behalten, wenn die ORM und SQL Ebene in der gleichen Transaktion. Im Grunde genommen von einer Seite, ORM Datenänderungen werden trafen nur die Datenbank, wenn Sie die Änderungen aus Ihrer Sitzung spülen. Von der anderen Seite beeinflussen SQL-Datenmanipulationsanweisungen nicht die Objekte, die in der Sitzung sind.

Wenn Sie also sagen,

for c in session.query(Stuff).all():
    c.foo = c.foo+1
session.commit()

wird es tun, was er sagt, alle Objekte aus der Datenbank holen gehen, um alle Objekte ändern und dann, wenn es an der Zeit, die Änderungen in der Datenbank zu spülen, aktualisieren Sie die Zeilen nacheinander zu übertragen.

Stattdessen sollten Sie dies tun:

session.execute(update(stuff_table, values={stuff_table.c.foo: stuff_table.c.foo + 1}))
session.commit()

Dies wird als eine Abfrage ausführen, wie man erwarten würde, und weil atleast die Standardsitzungskonfiguration alle Daten in der Sitzung auf begehen abläuft Sie haben keine veralteten Daten Probleme.

In der fast-released 0.5 Serie Sie diese Methode auch für die Aktualisierung verwenden:

session.query(Stuff).update({Stuff.foo: Stuff.foo + 1})
session.commit()

Das wird im Grunde die gleiche SQL-Anweisung wie die vorherige Schnipsel laufen, sondern auch die geänderten Zeilen auswählen und alle veralteten Daten in der Sitzung abläuft. Wenn Sie wissen, dass Sie keine Sitzungsdaten verwenden nach dem Update auch synchronize_session = False auf die Update-Anweisung hinzufügen könnte und loszuwerden, dass wählen.

Andere Tipps

session.query(Clients).filter(Clients.id == client_id_list).update({'status': status})
session.commit()

Versuchen Sie, diese =)

Es gibt mehrere Möglichkeiten, mit sqlalchemy UPDATE

1) for c in session.query(Stuff).all():
       c.foo += 1
   session.commit()

2) session.query().\
       update({"foo": (Stuff.foo + 1)})
   session.commit()

3) conn = engine.connect()
   stmt = Stuff.update().\
       values(Stuff.foo = (Stuff.foo + 1))
   conn.execute(stmt)

Hier ist ein Beispiel dafür, wie das gleiche Problem zu lösen, ohne die Felder zuordnen müssen manuell:

from sqlalchemy import Column, ForeignKey, Integer, String, Date, DateTime, text, create_engine
from sqlalchemy.exc import IntegrityError
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.orm import sessionmaker
from sqlalchemy.orm.attributes import InstrumentedAttribute

engine = create_engine('postgres://postgres@localhost:5432/database')
session = sessionmaker()
session.configure(bind=engine)

Base = declarative_base()


class Media(Base):
  __tablename__ = 'media'
  id = Column(Integer, primary_key=True)
  title = Column(String, nullable=False)
  slug = Column(String, nullable=False)
  type = Column(String, nullable=False)

  def update(self):
    s = session()
    mapped_values = {}
    for item in Media.__dict__.iteritems():
      field_name = item[0]
      field_type = item[1]
      is_column = isinstance(field_type, InstrumentedAttribute)
      if is_column:
        mapped_values[field_name] = getattr(self, field_name)

    s.query(Media).filter(Media.id == self.id).update(mapped_values)
    s.commit()

So eine Media-Instanz zu aktualisieren, können Sie etwas tun:

media = Media(id=123, title="Titular Line", slug="titular-line", type="movie")
media.update()

Withough Tests, ich würde versuchen:

for c in session.query(Stuff).all():
     c.foo = c.foo+1
session.commit()

(IIRC, commit () funktioniert ohne flush ()).

Ich habe festgestellt, dass manchmal eine große Abfrage tun und dann in Python Iterieren kann bis zu 2 Größenordnungen schneller als viele Abfragen. Ich gehe davon aus, dass über das Query-Objekt iteriert ist weniger effizient als durch die alle () -Methode des Query-Objekt erzeugt über eine Liste iterieren.

[Bitte beachten Sie Kommentar unten - diese Dinge beschleunigen gar nicht alle oben].

Wenn es aufgrund des Aufwands im Hinblick auf die Erstellung von Objekten ist, dann wahrscheinlich es kann gar nicht mit SA beschleunigt werden.

Wenn es ist, weil es ähnliche Objekte geladen, dann könnten Sie in der Lage sein, etwas mit verzögertes Laden zu tun. Gibt es viele Objekte erstellt aufgrund Referenzen werden? (IE, auch ein Firmenobjekt immer bekommt alle zugehörigen Personen-Objekte).

Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top