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

StackOverflow https://stackoverflow.com/questions/270879

  •  06-07-2019
  •  | 
  •  

سؤال

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

ويقول لقد حصلت على العمود 'فو' في قاعدة البيانات الخاصة بي وأريد أن زيادة عليه. في SQLite على التوالي، هذا أمر سهل:

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

وأنا أحسب ما يعادل SQLAlchemy SQL باني:

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 SQLAlchemy على ذلك؟ أو باستخدام أي ORM الثعبان الآخر؟ أو ينبغي أن مجرد العودة إلى كتابة SQL باليد؟

هل كانت مفيدة؟

المحلول

والمقصود 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 = خطأ لبيان التحديث والتخلص من هذا حدد.

نصائح أخرى

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

وهذه محاولة =)

وهناك عدة طرق لتحديث باستخدام sqlalchemy

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)

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

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

وذلك لتحديث مثيل وسائل الإعلام، يمكنك أن تفعل شيئا من هذا القبيل:

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

وWithough الاختبار، وسوف أحاول:

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

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

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

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

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

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

مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top