SQLAlchemy ORM का उपयोग करके डेटाबेस को कुशलतापूर्वक अपडेट करना


116

मैं एक नया एप्लिकेशन शुरू कर रहा हूं और विशेष रूप से SQLAlchemy - ORM का उपयोग कर रहा हूं।

कहें कि मुझे अपने डेटाबेस में एक कॉलम 'फू' मिला है और मैं इसे बढ़ाना चाहता हूं। सीधे साइक्लाइट में, यह आसान है:

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

यह सही काम करता है, लेकिन यह अन्य दो दृष्टिकोणों के मुकाबले पचास गुना कम समय लेता है। मुझे लगता है कि क्योंकि यह इसके साथ काम करने से पहले सभी डेटा को स्मृति में लाना है।

क्या SQLAlchemy के ORM का उपयोग करके कुशल SQL उत्पन्न करने का कोई तरीका है? या किसी अन्य अजगर ORM का उपयोग? या मैं बस हाथ से SQL लिखने के लिए वापस जाना चाहिए?


1
ठीक है, मैं मान रहा हूं कि उत्तर है "यह कुछ ऐसा नहीं है जिसे ओआरएम अच्छी तरह से करते हैं"। ओह अच्छा; मैं रहता हूं और सीखता हूं।
जॉन फूही

विभिन्न ओआरएम पर कुछ प्रयोग चलाए गए हैं और वे कैसे लोड और ड्यूरेस के तहत प्रदर्शन करते हैं। एक लिंक काम नहीं है, लेकिन पढ़ने लायक है।
मैथ्यू Schinckel

अंतिम (ORM) उदाहरण के साथ मौजूद एक और समस्या यह है कि यह परमाणु नहीं है ।
मैरियन

जवाबों:


181

SQLAlchemy का ORM 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()

यह मूल रूप से पिछले स्निपेट के रूप में एक ही एसक्यूएल स्टेटमेंट चलाएगा, लेकिन बदले हुए पंक्तियों का भी चयन करेगा और सत्र में किसी भी बासी डेटा को समाप्त कर सकता है। यदि आप जानते हैं कि आप अपडेट के बाद किसी भी सत्र डेटा का उपयोग नहीं कर रहे हैं तो आप synchronize_session=Falseअपडेट स्टेटमेंट में भी जोड़ सकते हैं और उस चयन से छुटकारा पा सकते हैं।


2
3 तरह से, क्या यह orm इवेंट (जैसे after_update) को ट्रिगर करेगा?
केन

@ नहीं, नहीं, यह नहीं होगा। Query.update docs.sqlalchemy.org/en/13/orm/… के लिए एपीआई डॉक्टर देखें । इसके बजाय आपके पास after_bulk_update docs.sqlalchemy.org/en/13/orm/… के
TrilceAC

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

यह कोशिश करो =)


इस विधि ने मेरे लिए काम किया। लेकिन समस्या इसकी धीमी है। यह कुछ 100k डेटा रिकॉर्ड के लिए समय का एक अच्छा टुकड़ा की जरूरत है। क्या कोई तेज़ तरीका है?
बैराठियास

बहुत बहुत धन्यवाद इस दृष्टिकोण ने मेरे लिए काम किया। यह वास्तव में बुरा है कि sqlachemy में jsonकॉलम को अपडेट करने का एक छोटा तरीका नहीं है
जय प्रकाश

6
इस पद्धति का उपयोग करते समय अभी भी प्रदर्शन करने वाले मुद्दों के लिए: डिफ़ॉल्ट रूप से यह पहले प्रत्येक रिकॉर्ड के लिए एक चयन कर सकता है, और केवल बाद में अद्यतन करें। सिंक्रोनाइज़ करना_सेशन = अपडेट करने का झूठा तरीका () विधि ऐसा होने से रोकती है, लेकिन यह सुनिश्चित करने के लिए केवल तभी करें जब आप उन वस्तुओं का उपयोग न करें जो आप प्रतिबद्ध होने से पहले फिर से अपडेट करते हैं ()।
तेनाबू

25

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)

6

मैन्युअल रूप से फ़ील्ड को मैप किए बिना उसी समस्या को हल करने का एक उदाहरण यहां दिया गया है:

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

1

पर्याप्त परीक्षण, मैं कोशिश करूँगा:

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

(IIRC, प्रतिबद्ध () फ्लश के बिना काम करता है) ()।

मैंने पाया है कि कई बार एक बड़ी क्वेरी करने और फिर अजगर में पुनरावृत्ति बहुत सारे प्रश्नों की तुलना में तेजी से 2 आदेशों तक हो सकती है। मुझे लगता है कि क्वेरी ऑब्जेक्ट पर पुनरावृति क्वेरी ऑब्जेक्ट के सभी () विधि द्वारा उत्पन्न सूची से अधिक पुनरावृत्ति करने से कम कुशल है।

[कृपया नीचे टिप्पणी करें - इससे चीजों में तेजी नहीं आई]।


2
.All () और रिमूवल (.flush) जोड़ना समय को बिल्कुल भी नहीं बदलता है।
जॉन फोहि

1

यदि ऑब्जेक्ट बनाने के मामले में यह ओवरहेड की वजह से है, तो संभवतः इसे एसए के साथ बिल्कुल भी नहीं बख्शा जा सकता है।

यदि ऐसा है क्योंकि यह संबंधित वस्तुओं को लोड कर रहा है, तो आप आलसी लोडिंग के साथ कुछ करने में सक्षम हो सकते हैं। क्या संदर्भों के कारण बहुत सारी वस्तुएं बन रही हैं? (IE, कंपनी ऑब्जेक्ट प्राप्त करने से संबंधित लोगों की सभी वस्तुएं भी मिल जाती हैं)।


नहीं, मेज के सभी अपने दम पर। मैंने पहले कभी भी ORM का उपयोग नहीं किया है - क्या यह कुछ ऐसा है जो वे खराब हैं?
जॉन फोहि

1
ऑब्जेक्ट बनाने के कारण एक ओवरहेड है, लेकिन मेरी राय में यह दंड के लायक है - एक डेटाबेस में लगातार वस्तुओं को स्टोर करने में सक्षम होना भयानक है।
मैथ्यू Schinckel
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.