python تحديث قاعدة البيانات باستخدام SQLAlchemy ORM بكفاءة




(5)

اختبار شامل ، كنت أحاول:

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

(IIRC ، ارتكاب () يعمل دون دافق ()).

لقد وجدت أنه في بعض الأحيان إجراء استعلام كبير ثم التكرار في python يمكن أن تصل إلى 2 أوامر من حجم أسرع من الكثير من الاستفسارات. أفترض أن تكرار كائن الاستعلام أقل كفاءة من تكرار عبر قائمة تم إنشاؤها بواسطة أسلوب all () كائن الاستعلام.

[يرجى ملاحظة التعليق أدناه - وهذا لم يسرع الأمور على الإطلاق].

أنا بدأت تطبيق جديد وتبحث في استخدام ORM - على وجه الخصوص ، SQLAlchemy.

قل لدي عمود "foo" في قاعدة البيانات الخاصة بي وأريد زيادته. في sqlite مستقيم ، هذا سهل:

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

فكنت أحسب ما يعادل SQLAlchemy SQL-builder:

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)

هذا أبطأ قليلاً ، لكن ليس هناك الكثير فيه.

هنا أفضل تخمين لاتباع نهج SQLAlchemy ORM:

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

هذا ما يفعل الشيء الصحيح ، ولكن الأمر يتطلب أقل من خمسين مرة فقط من المقاربتين الأخريين. أفترض ذلك لأنه يتعين عليه إحضار جميع البيانات إلى الذاكرة قبل أن تتمكن من استخدامها.

هل هناك أي طريقة لإنشاء SQL الفعال باستخدام ORM SQLSlchemy ل؟ أو استخدام أي بيثون أخرى ORM؟ أو يجب أن أعود إلى كتابة SQL باليد؟


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

جرب هذا =)


في ما يلي مثال لكيفية حل المشكلة نفسها دون الحاجة إلى تعيين الحقول يدويًا:

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://[email protected]: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()

لذلك لتحديث نسخة وسائط ، يمكنك القيام بشيء كالتالي:

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

إذا كان ذلك بسبب النفقات العامة من حيث إنشاء الكائنات ، فمن المحتمل أنه لا يمكن تسريعه على الإطلاق مع SA.

إذا كان السبب في ذلك هو تحميل كائنات ذات صلة ، فقد تتمكن من القيام بشيء ما باستخدام التحميل البطيء. هل هناك الكثير من الأشياء التي يتم إنشاؤها بسبب المراجع؟ (IE ، الحصول على كائن الشركة يحصل أيضًا على جميع كائنات الأشخاص ذات الصلة).


من المفترض استخدام ORM الخاص بـ SQLAlchemy مع طبقة SQL ، وليس إخفاؤه. ولكن عليك أن تأخذ في الاعتبار شيئًا أو شيئين عند استخدام ORM و SQL العادي في المعاملة نفسها. بشكل أساسي ، من جانب واحد ، ستقوم تعديلات بيانات ORM فقط بضرب قاعدة البيانات عند مسح التغييرات من جلسة العمل الخاصة بك. من الجانب الآخر ، لا تؤثر عبارات معالجة بيانات SQL على الكائنات الموجودة في جلسة العمل الخاصة بك.

لذلك إذا قلت

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

ستفعل ما تقوله ، وستذهب لجلب كل الكائنات من قاعدة البيانات ، وتعديل جميع الكائنات ، ثم عندما يحين وقت مسح التغييرات إلى قاعدة البيانات ، قم بتحديث الصفوف واحدة تلو الأخرى.

بدلا من ذلك يجب عليك القيام بذلك:

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

سيتم تنفيذ هذا كطلب بحث واحد كما تتوقع ، ونظرًا لأن إعدادات الجلسة الافتراضية تنتهي صلاحيتها لجميع البيانات في الجلسة ، فليس لديك أي مشكلات في البيانات القديمة.

في السلسلة 0.5 التي تم إصدارها تقريبًا ، يمكنك أيضًا استخدام هذه الطريقة للتحديث:

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

سيعمل هذا بشكل أساسي على استخدام جملة SQL نفسها التي كان بها المقتطف السابق ، ولكن أيضًا تحديد الصفوف التي تم تغييرها وإنهاء صلاحية أي بيانات قديمة في الجلسة. إذا كنت تعلم أنك لا تستخدم أي بيانات جلسة بعد التحديث ، يمكنك أيضًا إضافة synchronize_session = False لبيان التحديث والتخلص من ذلك التحديد.







sqlalchemy