Pandas: استخدم إدخالات الصفوف المتعددة لزيادة السرعة الهائلة في to_sql عبر اتصالات زمن الوصول العالي

تم إنشاؤها على ١ ديسمبر ٢٠١٤  ·  48تعليقات  ·  مصدر: pandas-dev/pandas

لقد كنت أحاول إدراج حوالي 30 ألف صف في قاعدة بيانات mysql باستخدام pandas-0.15.1 و oursql-0.9.3.1 و sqlalchemy-0.9.4. نظرًا لأن الجهاز يقع عبر المحيط الأطلسي مني ، فإن الاتصال data.to_sql يستغرق أكثر من ساعة واحدة لإدخال البيانات. عند الفحص باستخدام wireshark ، تكمن المشكلة في أنه يتم إرسال ملحق لكل صف ، ثم انتظار ACK قبل إرسال التالي ، وقصة طويلة قصيرة ، فإن أوقات ping تقتلني.

ومع ذلك ، باتباع الإرشادات من SQLAlchemy ، قمت بتغيير

def _execute_insert(self, conn, keys, data_iter):
    data = [dict((k, v) for k, v in zip(keys, row)) for row in data_iter]
    conn.execute(self.insert_statement(), data)

ل

def _execute_insert(self, conn, keys, data_iter):
    data = [dict((k, v) for k, v in zip(keys, row)) for row in data_iter]
    conn.execute(self.insert_statement().values(data))

وتكتمل العملية برمتها في أقل من دقيقة. (لتوفير نقرة ، يكون الاختلاف بين مكالمات متعددة إلى insert into foo (columns) values (rowX) ومكالمة واحدة ضخمة insert into foo (columns) VALUES (row1), (row2), row3) ). بالنظر إلى عدد المرات التي يُرجح فيها استخدام الباندا لإدخال كميات كبيرة من البيانات ، فإن هذا يبدو وكأنه فوز كبير سيكون من الرائع تضمينه على نطاق أوسع.

بعض التحديات:

  • لا تدعم كل قاعدة بيانات الإدخالات المتعددة (لم يكن SQLite و SQLServer في الماضي ، على الرغم من أنهما يعملان الآن). لا أعرف كيفية التحقق من ذلك عبر SQLAlchemy
  • لم يسمح لي خادم MySQL الذي كنت أستخدمه بإدخال البيانات دفعة واحدة ، واضطررت إلى ضبط حجم القطع (عملت 5 كيلو بشكل جيد ، لكن أعتقد أن 30 كيلو كانت أكثر من اللازم). إذا جعلنا هذا الإدخال الافتراضي ، فسيتعين على معظم الأشخاص إضافة حجم مقطع (قد يكون من الصعب حسابه ، حيث قد يتم تحديده من خلال الحد الأقصى لحجم حزمة الخادم).

أسهل طريقة للقيام بذلك ، هي إضافة المعامل المنطقي multirow= (الافتراضي False ) إلى الوظيفة to_sql ، ثم ترك المستخدم مسؤولاً عن ضبط حجم القطع ، ولكن ربما هناك طريقة أفضل؟

أفكار؟

IO SQL Performance

التعليق الأكثر فائدة

لقد توصلنا إلى كيفية تصحيح القرد - قد يكون مفيدًا لشخص آخر. احصل على هذا الرمز قبل استيراد الباندا.

from pandas.io.sql import SQLTable

def _execute_insert(self, conn, keys, data_iter):
    print "Using monkey-patched _execute_insert"
    data = [dict((k, v) for k, v in zip(keys, row)) for row in data_iter]
    conn.execute(self.insert_statement().values(data))

SQLTable._execute_insert = _execute_insert

ال 48 كومينتر

هذا يبدو معقولا. شكرا للتحقيق في هذا!

بالنسبة للتنفيذ ، سيعتمد ذلك على كيفية تعامل sqlalchemy مع نكهات قاعدة البيانات التي لا تدعم هذا (لا يمكنني اختبار هذا في الوقت الحالي ، ولكن يبدو أن sqlalchemy يثير خطأ (على سبيل المثال http://stackoverflow.com/questions/ 23886764 / multi-insert-statement-in-mssql-with-sqlalchemy). أيضًا ، إذا كانت النتيجة أن الكثير من الأشخاص سيضطرون إلى تعيين حجم القطع ، فهذه في الواقع ليست فكرة جيدة للقيام بها كإعداد افتراضي (إلا إذا قمنا بتعيين يقطع إلى قيمة بشكل افتراضي).
لذا قد تبدو إضافة كلمة رئيسية أفضل.

تضمين التغريدة _

يبدو أن SQLAlchemy لديها علامة dialect.supports_multivalues_insert (انظر على سبيل المثال http://pydoc.net/Python/SQLAlchemy/0.8.3/sqlalchemy.sql.compiler/ ، ربما يُطلق عليها supports_multirow_insert في الإصدارات الأخرى ، https : //www.mail-archive.com/[email protected]/msg202880.html).

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

الآن أنا فقط بحاجة إلى إقناع الأشخاص في SQLAlchemy بتعيين supports_multivalues_insert إلى true على SQL Server> 2005 (لقد اخترقتها في الكود وهي تعمل بشكل جيد ، لكنها ليست قيد التشغيل افتراضيًا).

في ملاحظة متعلقة بالموضوع ، أعتقد أن حجم القطع قد يكون صعبًا. في إعداد mysql الخاص بي (والذي ربما قمت بتكوينه للسماح بالحزم الكبيرة) ، يمكنني تعيين chunksize = 5000 ، في إعداد SQLServer الخاص بي ، كان 500 كبيرًا جدًا ، لكن 100 يعمل بشكل جيد. ومع ذلك ، ربما يكون صحيحًا أن معظم الفوائد من هذه التقنية تأتي من الانتقال من إدخال صف واحد في كل مرة إلى 100 ، بدلاً من 100 إلى 1000.

ماذا لو كان chunksize=None يعني "اختر حجمًا متكيفًا"؟ حاول شيئًا مثل 5000 ، 500 ، 50 ، 1. يمكن للمستخدمين إيقاف هذا عن طريق تحديد حجم القطع. إذا كانت النفقات العامة من هذه المحاولات كبيرة جدًا ، فأنا أحب اقتراح @ maxgrenderjones : chunksize=10 هو الخيار الافتراضي الأفضل من chunksize=1 .

في هذا التعليق الأخير " chunksize=10 هو خيار افتراضي أفضل من chunksize=1 " -> وهذا ليس صحيحًا تمامًا على ما أعتقد. الوضع الحالي هو تنفيذ جملة تنفيذية واحدة تتكون من عبارات إدراج متعددة الأسطر لصف واحد (وهي ليست حجمًا مقطوعًا من 1) ، بينما يعني chunksize=10 تنفيذ الكثير من جمل التنفيذ مع كل مرة صف واحد متعدد الصفوف إدراج.
ولا أعرف ما إذا كان هذا أسرع بالضرورة ، لكن الكثير يعتمد على الموقف. على سبيل المثال مع الكود الحالي وقاعدة بيانات sqlite المحلية:

In [4]: engine = create_engine('sqlite:///:memory:') #, echo='debug')

In [5]: df = pd.DataFrame(np.random.randn(50000, 10))

In [6]: %timeit df.to_sql('test_default', engine, if_exists='replace')
1 loops, best of 3: 956 ms per loop

In [7]: %timeit df.to_sql('test_default', engine, if_exists='replace', chunksize=10)
1 loops, best of 3: 2.23 s per loop

لكن هذا بالطبع لا يستخدم ميزة الصفوف المتعددة

لقد توصلنا إلى كيفية تصحيح القرد - قد يكون مفيدًا لشخص آخر. احصل على هذا الرمز قبل استيراد الباندا.

from pandas.io.sql import SQLTable

def _execute_insert(self, conn, keys, data_iter):
    print "Using monkey-patched _execute_insert"
    data = [dict((k, v) for k, v in zip(keys, row)) for row in data_iter]
    conn.execute(self.insert_statement().values(data))

SQLTable._execute_insert = _execute_insert

ربما يمكننا البدء بإضافة هذه الميزة من خلال كلمة رئيسية جديدة multirow=True (مع افتراضي False في الوقت الحالي) ، ومن ثم يمكننا لاحقًا معرفة ما إذا كان بإمكاننا تمكينها افتراضيًا؟

maxgrenderjonesnhockham مهتم بعمل علاقات عامة لإضافة هذا؟

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

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

أود أن أقترح إنشاء فئات فرعية من قاعدة بيانات SQL و SQLTable لمعالجة تطبيقات الأداء المحددة ، حيث سيتم استخدامها من خلال واجهة برمجة التطبيقات (API) الموجهة للكائنات. ربما يمكن إضافة طريقة "تبديل الواجهة الخلفية" ولكن بصراحة استخدام OO api بسيط للغاية ، لذا فمن المحتمل أن يكون هذا مبالغة بالنسبة لما هو بالفعل حالة استخدام متخصصة.

لقد أنشأت مثل هذه الفئة الفرعية لتحميل مجموعات البيانات الكبيرة إلى Postgres (إنه في الواقع أسرع بكثير في حفظ البيانات إلى CSV ثم استخدام أوامر النسخ غير القياسية المضمنة من sql بدلاً من استخدام الإدخالات ، راجع https: //gist.github. com / mangecoeur / 1fbd63d4758c2ba0c470 # file-pandas_postgres-py). لاستخدامه ، ما عليك سوى استخدام PgSQLDatabase(engine, <args>).to_sql(frame, name,<kwargs>)

للإشارة فقط ، حاولت تشغيل الكود بواسطة jorisvandenbossche (آخر 3 ديسمبر) باستخدام ميزة multirow. إنه أبطأ قليلاً. لذا فإن مقايضات السرعة هنا ليست تافهة:

In [4]: engine = create_engine('sqlite:///:memory:') #, echo='debug')

In [5]: df = pd.DataFrame(np.random.randn(50000, 10))

In [6]: 

In [6]: %timeit df.to_sql('test_default', engine, if_exists='replace')
1 loops, best of 3: 1.05 s per loop

In [7]: 

In [7]: from pandas.io.sql import SQLTable

In [8]: 

In [8]: def _execute_insert(self, conn, keys, data_iter):
   ...:         data = [dict((k, v) for k, v in zip(keys, row)) for row in data_iter]
   ...:         conn.execute(self.insert_statement().values(data))
   ...:     

In [9]: SQLTable._execute_insert = _execute_insert

In [10]: 

In [10]: reload(pd)
Out[10]: <module 'pandas' from '/usr/local/lib/python2.7/site-packages/pandas/__init__.pyc'>

In [11]: 

In [11]: %timeit df.to_sql('test_default', engine, if_exists='replace', chunksize=10)
1 loops, best of 3: 9.9 s per loop

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

كما كنت أظن. لم يكن الترقيع القرد هو الحل الذي كنت أقترحه - بدلاً من ذلك نشحن عددًا من الفئات الفرعية الموجهة نحو الأداء والتي يمكن للمستخدم المطلع استخدامها من خلال واجهة OO (لتجنب تحميل واجهة برمجة التطبيقات الوظيفية مع العديد من الخيارات)

-----رسالة أصلية-----
من: "Artemy Kolchinsky" [email protected]
تاريخ الإرسال: 26/02/2015 الساعة 17:13
إلى: "pydata / pandas" [email protected]
نسخة إلى: "mangecoeur" جون. [email protected]
الموضوع: Re: [الباندا] استخدم إدخالات الصفوف المتعددة لزيادة السرعة الهائلة في اتصالات to_sqlover بزمن انتقال عالٍ (# 8953)

للإشارة فقط ، حاولت تشغيل الكود بواسطة jorisvandenbossche (آخر 3 ديسمبر) باستخدام ميزة multirow. إنه أبطأ قليلاً. لذا فإن مقايضات السرعة هنا ليست تافهة:
في [4]: ​​engine = create_engine ('sqlite: ///: memory:') #، echo = 'debug')

في [5]: df = pd.DataFrame (np.random.randn (50000، 10))

في [6]:

في [6]:٪ timeit df.to_sql ('test_default' ، المحرك ، if_exists = 'replace')
1 حلقات ، أفضل 3: 1.05 ثانية لكل حلقة

في [7]:

في [7]: من pandas.io.sql استيراد SQLTable

في [8]:

في [8]: def _execute_insert (self، conn، keys، data_iter):
...: data = [dict ((k، v) for k، v in zip (keys، row)) للصف في data_iter]
...: conn.execute (self.insert_statement (). قيم (بيانات))
...:

في [9]: SQLTable._execute_insert = _execute_insert

في [10]:

في [10]: إعادة تحميل (pd)
خارج [10]:

في [11]:

في [11]:٪ timeit df.to_sql ('test_default'، engine، if_exists = 'replace'، chunksize = 10)
1 حلقات ، أفضل من 3: 9.9 ثانية لكل حلقة
أيضًا ، أوافق على أن إضافة معلمات الكلمات الرئيسية أمر محفوف بالمخاطر. ومع ذلك ، فإن ميزة multirow تبدو أساسية جدًا. أيضًا ، ربما لا يكون "الترقيع القرد" أكثر قوة في تغييرات واجهة برمجة التطبيقات من معلمات الكلمات الرئيسية.
-
قم بالرد على هذا البريد الإلكتروني مباشرة أو قم بعرضه على GitHub.

وفقًا لعنوان التذكرة الأولي ، لا أعتقد أن هذا النهج سيكون مفضلاً في جميع الحالات ، لذلك لن أجعله الافتراضي. ومع ذلك ، بدونها ، فإن الباندا to_sql غير قابلة للاستخدام بالنسبة لي ، لذلك من المهم بما يكفي بالنسبة لي لمواصلة طلب التغيير. (لقد أصبح أيضًا أول شيء أغيره عندما أقوم بترقية إصدار الباندا). بالنسبة إلى قيم chunksize المعقولة ، لا أعتقد أن هناك قيمة حقيقية واحدة n ، حيث سيعتمد حجم الحزمة على عدد الأعمدة الموجودة (وما بداخلها) بطرق يصعب التنبؤ بها . لسوء الحظ ، فشل SQLServer في ظهور رسالة خطأ تبدو غير مرتبطة تمامًا (ولكنها ليست كذلك) إذا قمت بتعيين chunksize مرتفعًا جدًا (وهذا على الأرجح سبب عدم تشغيل إدخالات multirow إلا باستخدام تصحيح في SQLAlchemy) ، ولكن يعمل بشكل جيد مع mysql . قد يحتاج المستخدمون إلى تجربة لتحديد قيمة n التي من المحتمل أن ينتج عنها حجم حزمة كبير مقبول (مهما كانت قاعدة البيانات الداعمة الخاصة بهم). اختيار الباندا n من المحتمل أن يجعلنا أكثر انخفاضًا في تفاصيل التنفيذ أكثر مما نريد (أي الاتجاه المعاكس لأقصى قدر ممكن من التجريد نهج SQLALchemy)

باختصار ، توصيتي بإضافتها ككلمة رئيسية ، مع بعض التعليقات المفيدة حول كيفية استخدامها. لن تكون هذه هي المرة الأولى التي يتم فيها استخدام كلمة رئيسية لتحديد تطبيق (انظر: http://pandas.pydata.org/pandas-docs/dev/generated/pandas.DataFrame.apply.html) ولكن ربما هذا ليس ' أفضل مثال ، حيث لم تكن لدي فكرة عن معنى raw= ، حتى بعد قراءة الشرح!

لقد لاحظت أنه يستهلك أيضًا قدرًا كبيرًا من الذاكرة. مثل DataFrame 1.6+ غيغابايت مع حوالي 700000 صف و 301 عمود يتطلب ما يقرب من 34 غيغابايت أثناء الإدراج! هذا هو مثل أكثر من غير فعال. أي أفكار حول لماذا قد يكون هذا هو الحال؟ هنا مقطع شاشة:

image

اهلا ياجماعة،
أي تقدم في هذه المسألة؟

أحاول إدخال حوالي 200 ألف صف باستخدام to_sql لكن الأمر يستغرق وقتًا طويلاً ويستهلك قدرًا هائلاً من الذاكرة! يساعد استخدام chuncksize في الذاكرة ولكن لا تزال السرعة بطيئة جدًا.

انطباعي ، بالنظر إلى تتبع MSSQL DBase هو أن الإدراج يتم في الواقع على صف واحد في ذلك الوقت.

الطريقة الوحيدة القابلة للتطبيق الآن هي التفريغ إلى ملف csv في مجلد مشترك واستخدام BULK INSERT. لكنها مزعجة للغاية وغير أنيقة!

andreacassioli يمكنك استخدام odo لإدراج DataFrame في قاعدة بيانات SQL من خلال ملف CSV وسيط. انظر تحميل ملفات CSV إلى قواعد بيانات SQL .

لا أعتقد أنه يمكنك الاقتراب من أداء BULK INSERT باستخدام ODBC.

ostrokach شكرًا لك ، في الواقع أنا أستخدم ملفات csv الآن. إذا تمكنت من الاقتراب ، فسأتبادل بعض الوقت من أجل البساطة!

اعتقدت أن هذا قد يساعد شخصًا ما:
http://docs.sqlalchemy.org/en/latest/faq/performance.html#i -m-inserting-400-000-rows-with-the-orm-and-it-s-really-slow

indera pandas لا تستخدم ORM ، فقط sqlalchemy Core (وهو ما يقترح إدخال المستند هناك لاستخدامه للإدخالات الكبيرة)

هل هناك أي إجماع حول كيفية التغلب على هذا في هذه الأثناء؟ أقوم بإدخال عدة ملايين من الصفوف في postgres ويستغرق الأمر إلى الأبد. هل CSV / odo هو السبيل للذهاب؟

russlamb طريقة عملية لحل هذه المشكلة هي ببساطة التحميل المجمع. هذا شخص خاص بالديسيبل ، لذا أعتقد أن odo لديه حلول لـ postgresl (وقد يكون mysql ). لشيء مثل sqlserver ، عليك أن "تفعل هذا بنفسك" (IOW عليك كتابته).

بالنسبة لـ sqlserver ، استخدمت برنامج تشغيل FreeTDS (http://www.freetds.org/software.html و https://github.com/mkleehammer/pyodbc) مع كيانات SQLAlchemy مما أدى إلى إدخالات سريعة جدًا (20 ألف صف لكل إطار بيانات) :

from sqlalchemy.ext.declarative import declarative_base
Base = declarative_base()


class DemographicEntity(Base):
    __tablename__ = 'DEMOGRAPHIC'

    patid = db.Column("PATID", db.Text, primary_key=True)
    """
    patid = db.Column("PATID", db.Text, primary_key=True, autoincrement=False, nullable=True)
    birth_date = db.Column("BIRTH_DATE", db.Date)
    birth_time = db.Column("BIRTH_TIME", db.Text(5))
    sex = db.Column("SEX", db.Text(2))

def get_db_url(db_host, db_port, db_name, db_user, db_pass):
    params = parse.quote(
        "Driver={{FreeTDS}};Server={};Port={};"
        "Database={};UID={};PWD={};"
        .format(db_host, db_port, db_name, db_user, db_pass))
    return 'mssql+pyodbc:///?odbc_connect={}'.format(params)

def get_db_pool():
    """
    Create the database engine connection.
    <strong i="6">@see</strong> http://docs.sqlalchemy.org/en/latest/core/engines.html

    :return: Dialect object which can either be used directly
            to interact with the database, or can be passed to
            a Session object to work with the ORM.
    """
    global DB_POOL

    if DB_POOL is None:
        url = get_db_url(db_host=DB_HOST, db_port=DB_PORT, db_name=DB_NAME,
                         db_user=DB_USER, db_pass=DB_PASS)
        DB_POOL = db.create_engine(url,
                                   pool_size=10,
                                   max_overflow=5,
                                   pool_recycle=3600)

    try:
        DB_POOL.execute("USE {db}".format(db=DB_NAME))
    except db.exc.OperationalError:
        logger.error('Database {db} does not exist.'.format(db=DB_NAME))

    return DB_POOL


def save_frame():
    db_pool = get_db_pool()
    records = df.to_dict(orient='records')
    result = db_pool.execute(entity.__table__.insert(), records)

هل CSV / odo هو السبيل للذهاب؟

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

ولكن ، russlamb ، من المثير للاهتمام دائمًا معرفة ما إذا كانت هذه الكلمة الرئيسية متعددة الصفوف ستشكل تحسينًا في حالتك. انظر على سبيل المثال https://github.com/pandas-dev/pandas/issues/8953#issuecomment -76139975 لمعرفة طريقة لاختبار ذلك بسهولة.

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

jorisvandenbossche يذكر المستند الذي ربطته أعلاه "بدلاً من ذلك ، تقدم SQLAlchemy ORM مجموعة طرق العمليات المجمعة ، والتي توفر روابط في الأقسام الفرعية لوحدة عملية العمل من أجل إصدار بنيات INSERT و UPDATE على المستوى الأساسي بدرجة صغيرة من ORM الأتمتة المستندة إلى ".

ما أقترحه هو تنفيذ إصدار محدد من sqlserver مقابل to_sql والذي يستخدم تحت غطاء المحرك SQLAlchemy ORMs للتسريع كما في الكود الذي نشرته أعلاه.

تم اقتراح هذا من قبل. الطريقة التي تذهب إليها هي تنفيذ SQL الباندا
فئة محسّنة للواجهة الخلفية. لقد نشرت جوهرًا في الماضي للاستخدام
نسخ postgres من الأمر وهو أسرع بكثير. لكن شيء مشابه
متوفر الآن في odo ، وتم بناؤه بطريقة أكثر قوة. ليس هناك الكثير
نقطة IMHO في تكرار العمل من odo.

في 7 مارس 2017 00:53 ، كتب "Andrei Sura" [email protected] :

jorisvandenbossche https://github.com/jorisvandenbossche المستند
لقد ربطت ما ورد أعلاه "بدلاً من ذلك ، تقدم SQLAlchemy ORM الجملة
مجموعة طرق العمليات ، والتي توفر روابط في الأقسام الفرعية من
وحدة عملية العمل من أجل إصدار المستوى الأساسي INSERT و UPDATE
يبني بدرجة صغيرة من الأتمتة القائمة على ORM. "

ما أقترحه هو تنفيذ إصدار محدد من sqlserver لـ
"to_sql" والذي يستخدم تحت غطاء المحرك نواة SQLAlchemy للتعجيلات.

-
أنت تتلقى هذا لأنه تم ذكرك.
قم بالرد على هذا البريد الإلكتروني مباشرة ، وقم بعرضه على GitHub
https://github.com/pandas-dev/pandas/issues/8953#issuecomment-284437587 ،
أو كتم الخيط
https://github.com/notifications/unsubscribe-auth/AAtYVDXKLuTlsh9ycpMQvU5C0hs_RxuYks5rjCwBgaJpZM4DCjLh
.

لاحظت أيضًا أنك ذكرت أنه يمكن لـ sqlalchemy الأساسية بدلاً من ذلك. ما لم يكن هناك شيء
لقد تغير كثيرًا ، يتم استخدام جوهر sqlalchemy فقط في أي حال ، لا orm. اذا أنت
تريد تسريع أكثر من استخدام النواة ، عليك الانتقال إلى المستوى الأدنى ، ديسيبل
تحسين محدد

في 7 مارس 2017 00:53 ، كتب "Andrei Sura" [email protected] :

jorisvandenbossche https://github.com/jorisvandenbossche المستند
لقد ربطت ما ورد أعلاه "بدلاً من ذلك ، تقدم SQLAlchemy ORM الجملة
مجموعة طرق العمليات ، والتي توفر روابط في الأقسام الفرعية من
وحدة عملية العمل من أجل إصدار المستوى الأساسي INSERT و UPDATE
يبني بدرجة صغيرة من الأتمتة القائمة على ORM. "

ما أقترحه هو تنفيذ إصدار محدد من sqlserver لـ
"to_sql" والذي يستخدم تحت غطاء المحرك نواة SQLAlchemy للتعجيلات.

-
أنت تتلقى هذا لأنه تم ذكرك.
قم بالرد على هذا البريد الإلكتروني مباشرة ، وقم بعرضه على GitHub
https://github.com/pandas-dev/pandas/issues/8953#issuecomment-284437587 ،
أو كتم الخيط
https://github.com/notifications/unsubscribe-auth/AAtYVDXKLuTlsh9ycpMQvU5C0hs_RxuYks5rjCwBgaJpZM4DCjLh
.

هل هذا يتم إصلاحه / العناية به؟ اعتبارًا من الآن ، يعد إدخال إطارات بيانات الباندا في SQL ديسيبل بطيئًا للغاية ما لم يكن إطار بيانات لعبة. دعونا نقرر الحل وندفعه إلى الأمام؟

dfernan كما هو مذكور أعلاه ، قد ترغب في إلقاء نظرة على odo . سيظل استخدام ملف CSV وسيط دائمًا أسرع من حيث يتم إجراء sqlalchemy ، بغض النظر عن نوع التحسينات التي تحدث هنا ...

ostrokach ، أنا غير مقتنع بأن سلوك odo هو ما يريده مستخدم الباندا النموذجي. من المحتمل أن تكون عمليات الإدراج متعددة الصفوف عبر ODBC سريعة بما يكفي لمعظم المحللين.

للتحدث عن نفسي ، قضيت بضع ساعات في التبديل من رقعة القرد أعلاه إلى odo. كان وقت تشغيل الباندا العادي أكثر من 10 ساعات ، RBAR. يعمل التصحيح القرد في 2 على نفس مجموعة البيانات.
كان مسار odo / CSV أسرع ، كما هو متوقع ، ولكن ليس بما يكفي لجعله يستحق الجهد المبذول. لقد تلاعبت بقضايا تحويل CSV التي لم أهتم بها كثيرًا ، كل ذلك باسم تجنب رقعة القرد. أقوم باستيراد 250 ألف صف من ~ 10 mysql و PG DB إلى منطقة مشتركة في Postgres ، لتحليل البرمجة اللغوية العصبية.

أنا على دراية وثيقة بمقاربات التحميل السائبة التي يتبناها odo. لقد استخدمتها لسنوات حيث بدأت ببيانات CSV. هناك قيود رئيسية عليهم:

  1. بالنسبة لحالة df-> CSV-> Postgres ، يلزم الوصول إلى shell وخطوة scp للحصول على ملف CSV على مضيف PG. يبدو أن mangecoeur قد تعرف على هذا الأمر من خلال دفق إلى STDIN.
  2. من أجل هدفي (250 ألف صف من التعليقات ، مع الكثير من الحالات الخاصة في محتوى النص) كافحت للحصول على معلمات CSV بشكل صحيح. لم أكن أرغب في تحقيق مكاسب في الأداء بشكل سيئ بما يكفي لمواصلة الاستثمار في هذا الأمر.

أعود إلى التصحيح ، حتى أتمكن من متابعة العمل التحليلي.

أتفق مع jorisvandenbossche و maxgrenderjones. سيكون وجود خيار (ليس افتراضيًا) لاختيار هذا مفيدًا للغاية. قد تجعل وجهة نظر artemyk حول dialect.supports_multivalues_insert هذا الأمر افتراضيًا معقولاً.

يسعدني تقديم PR إذا كان ذلك سيمضي قدمًا.

فقط لإضافة تجربتي مع odo ، لم تنجح مع إدراجات MS Sql الكبيرة بسبب مشكلة معروفة في الترميز. يعد إدراج imho m-row حلاً عمليًا جيدًا لمعظم الأشخاص.

markschwarz خيار لتمكين هذا للعمل بشكل أسرع سيكون موضع ترحيب كبير!

بتتبع الاستعلامات باستخدام sqlite ، يبدو أنني أتلقى إدخالات متعددة عند استخدام chunksize :

2017-09-28 00:21:39,007 INFO sqlalchemy.engine.base.Engine INSERT INTO country_hsproduct_year (location_id, product_id, year, export_rca, import_value, cog, export_value, distance, location_level, product_level) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
2017-09-28 00:21:39,007 INFO sqlalchemy.engine.base.Engine ((75, 1237, 1996, 1.7283086776733398, 273487116.0, 0.0, 514320160.0, 0.5413745641708374, 'country', '4digit'), (75, 1237, 1997, 1.7167805433273315, 312047528.0, 0.0, 592372864.0, 0.5314807891845703, 'country', '4digit'), (75, 1237, 1998, 1.2120152711868286, 341676961.0, 0.0, 468860608.0, 0.5472233295440674, 'country', '4digit'), (75, 1237, 1999, 1.236651062965393, 334604240.0, 0.0, 440722336.0, 0.5695921182632446, 'country', '4digit'), (75, 1237, 2000, 1.189828872680664, 383555023.0, 0.0, 426384832.0, 0.5794379711151123, 'country', '4digit'), (75, 1237, 2001, 0.9920380115509033, 374157144.0, 0.3462945520877838, 327031392.0, 0.6234743595123291, 'country', '4digit'), (75, 1237, 2002, 1.0405025482177734, 471456583.0, 0.0, 377909376.0, 0.6023964285850525, 'country', '4digit'), (75, 1237, 2003, 1.147829532623291, 552441401.0, 0.0, 481313504.0, 0.5896202325820923, 'country', '4digit')  ... displaying 10 of 100000 total bound parameter sets ...  (79, 1024, 2015, 0.0, None, 0.8785018920898438, 0.0, 0.9823430776596069, 'country', '4digit'), (79, 1025, 1995, 0.0, None, 0.5624096989631653, 0.0, 0.9839603304862976, 'country', '4digit'))

(بدون رقعة القرد ، أي)

ومن المثير للاهتمام ، مع رقعة القرد ، أنها تنكسر عندما أعطيتها قطع بحجم 10 ^ 5 ، لكن ليس 10 ^ 3. الخطأ هو "متغيرات SQL كثيرة جدًا" على sqlite.

makmanalp ، لم أتتبع على PG للتحقق من هذا السلوك ، لكنني دائمًا ما أقوم بتعيين chunksize على الإدراج. في المثال أعلاه ، قمت بتعيينه عشوائيًا على 5 قيم بين 200-5000. لم أجد فروقًا جذرية في الوقت المنقضي بين تلك الاختيارات ، بدون رقعة القرد. مع التصحيح ، انخفض الوقت المنقضي ~ 80٪.

https://github.com/pandas-dev/pandas/issues/8953#issuecomment -76139975

هل ما زالت رقعة القرد هذه تعمل؟ لقد جربته على MS SQL Server لكنني لم أجد أي تحسن. أيضا ، فإنه يطرح استثناء:

(pyodbc.Error) ('07002', '[07002] [Microsoft][SQL Server Native Client 11.0]COUNT field incorrect or syntax error (0) (SQLExecDirectW)')

hangyao أعتقد أن التصحيح خاص بالتنفيذ ، أحد تلك الأشياء التي يتركها Python DBAPI لبرنامج التشغيل DBAPI حول كيفية التعامل معه. لذلك يمكن أن يكون أسرع أم لا. RE: خطأ نحوي ، لست متأكدًا من ذلك.

أفكر في إضافة الوظيفة أدناه في الملف /io/sql.py في السطر 507 داخل الدالة _engine_builder داخل وظيفة IF clouse الجديدة على السطر 521 ، مما يجعل _engine_builder هو التالي مقتطف. لقد اختبرت ذلك لفترة وجيزة على بيئتي وهو يعمل بشكل رائع مع قواعد بيانات MSSQL ، حيث حقق أكثر من 100 مرة من السرعة. لم أختبره على قواعد بيانات أخرى حتى الآن.

الشيء الذي يجعلني أقوم بعمل علاقات عامة هو أنني أعتقد أنه من المجهود الإضافي جعله أنيقًا وآمنًا أكثر من مجرد إدخاله كما هو موضح أدناه ، فقد لا يكون هذا دائمًا هو المواصفات المطلوبة وإضافة مفتاح منطقي ، والذي يعمل على تشغيل / إيقاف تشغيل هذا الإعداد ، (على سبيل المثال ، fast_executemany=True ) في to_sql بدا وكأنه مسعى كبير جدًا للقيام به دون طلب ، أعتقد.

لذا فإن أسئلتي هي:

  • هل تعمل الوظيفة أدناه وتزيد أيضًا من سرعة INSERT لـ PostgreSQL؟

  • هل حدث الباندا يريد هذا المقتطف في مصدره؟ لو ذلك:

  • هل ترغب في إضافة هذه الوظيفة إلى الوظيفة الافتراضية sql.py أم أن هناك مكانًا أفضل لإضافة هذا؟

أحب أن أسمع بعض التعليقات.

مصدر الجواب: https://stackoverflow.com/questions/48006551/speeding-up-pandas-dataframe-to-sql-with-fast-executemany-of-pyodbc/48861231#48861231

@event.listens_for(engine, 'before_cursor_execute')
def receive_before_cursor_execute(conn, cursor, statement, params, context, executemany):
    if executemany:
        cursor.fast_executemany = True
def _engine_builder(con):
    """
    Returns a SQLAlchemy engine from a URI (if con is a string)
    else it just return con without modifying it.
    """
    global _SQLALCHEMY_INSTALLED
    if isinstance(con, string_types):
        try:
            import sqlalchemy
        except ImportError:
            _SQLALCHEMY_INSTALLED = False
        else:
            con = sqlalchemy.create_engine(con)

    @event.listens_for(engine, 'before_cursor_execute')
    def receive_before_cursor_execute(conn, cursor, statement, params, context, executemany):
        if executemany:
            cursor.fast_executemany = True
return con

@ tsktsktsk123 تم مؤخرًا دمج علاقات عامة متعلقة بهذا: https://github.com/pandas-dev/pandas/pull/19664. لم ألق نظرة بالتفصيل على رسالتك ، وهي بالتأكيد ليست هي نفسها تمامًا (تستخدم السمة supports_multivalues_insert لمحرك sqlalchemy) ، ولكن فقط للتأكد من أنك على دراية بها في حال كان هذا مفيدًا بالفعل كذلك.

هذه أخبار رائعة! لم ألق نظرة على العلاقات العامة لكنني سأقارنها في نهاية هذا الأسبوع وأعود بالنتائج. وذلك بفضل لرؤساء متابعة.

كنت أحاول فقط 0.23.0 RC2 (على postgresql) وبدلاً من زيادة الأداء ، أصبح البرنامج النصي أبطأ بشكل ملحوظ. أصبح استعلام قاعدة البيانات أسرع كثيرًا ولكن قياس الوقت لـ to_sql() أصبح في الواقع أبطأ بمقدار 1.5 مرة (مثل من 7 إلى 11 ثانية) ...

لست متأكدًا من أن التباطؤ يأتي من هذا العلاقات العامة رغم أنني اختبرت للتو RC.

أي شخص آخر واجه نفس المشكلة؟

@ schettino72 ما مقدار البيانات التي كنت تقوم بإدخالها؟

حوالي 30 ألف صف مع 10 أعمدة. ولكن في الحقيقة أي شيء أحاوله يكون أبطأ (SQL أسرع ولكن بشكل عام أبطأ). إنه يقوم بإنشاء جملة SQL ضخمة حيث يوجد استيفاء للقيمة لكل قيمة. شيء مثل

 %(user_id_m32639)s, %(event_id_m32639)s, %(colx_m32639)s,

لقد وجدت d6tstack أبسط بكثير في الاستخدام ، فهو سطر واحد d6tstack.utils.pd_to_psql(df, cfg_uri_psql, 'benchmark', if_exists='replace') وهو أسرع بكثير من df.to_sql() . يدعم postgres و mysql. راجع https://github.com/d6t/d6tstack/blob/master/examples-sql.ipynb

أستخدم حل Monkey Patch:

from pandas.io.sql import SQLTable

def _execute_insert(self, conn, keys, data_iter):
    print "Using monkey-patched _execute_insert"
    data = [dict((k, v) for k, v in zip(keys, row)) for row in data_iter]
    conn.execute(self.insert_statement().values(data))

SQLTable._execute_insert = _execute_insert

لبعض الوقت الآن ، لكنني أتلقى خطأً الآن:

TypeError: insert_statement() missing 2 required positional arguments: 'data' and 'conn'

هل يتلقى احد غيري هذا؟ أنا على Python 3.6.5 (Anaconda) والباندا == 0.23.0

هل يتم إصلاح هذا؟ حاليًا ، يعد df.to_sql بطيئًا للغاية ولا يمكن استخدامه على الإطلاق للعديد من حالات الاستخدام العملي. يبدو أن مشروع Odo قد تم التخلي عنه بالفعل.
لدي حالات الاستخدام التالية في السلاسل الزمنية المالية حيث يكون df.to_sql غير قابل للاستخدام إلى حد كبير:
1) نسخ بيانات csv التاريخية إلى قاعدة بيانات postgres - لا يمكن استخدام df.to_sql وكان عليه استخدام رمز مخصص حول وظيفة psycopg2 copy_from
2) تدفق البيانات (قادم في دفعة من 500-3000 صف في الثانية) ليتم إلقاؤها في قاعدة بيانات postgres - مرة أخرى أداء df.to_sql مخيب للآمال للغاية حيث يستغرق الأمر وقتًا طويلاً لإدخال هذه المجموعات الطبيعية من البيانات إلى postgres.
المكان الوحيد الذي أجد فيه df.to_sql مفيدًا الآن هو إنشاء الجداول تلقائيًا !!! - وهي ليست حالة الاستخدام المصممة من أجلها.
لست متأكدًا مما إذا كان هناك أشخاص آخرون يشاركونك نفس الاهتمام ولكن هذه المشكلة تحتاج إلى بعض الاهتمام لواجهات "إطارات البيانات إلى قاعدة البيانات" للعمل بسلاسة.
أتطلع لذلك.

مرحبًا ، أتلقى هذا الخطأ عندما أحاول إجراء إدراج متعدد في قاعدة بيانات SQLite:

هذا هو الكود الخاص بي:
df.to_sql("financial_data", con=conn, if_exists="append", index=False, method="multi")

وأنا أتلقى هذا الخطأ:

Traceback (most recent call last):

  File "<ipython-input-11-cf095145b980>", line 1, in <module>
    handler.insert_financial_data_from_df(data, "GOOG")

  File "C:\Users\user01\Documents\Code\FinancialHandler.py", line 110, in insert_financial_data_from_df
    df.to_sql("financial_data", con=conn, if_exists="append", index=False, method="multi")

  File "C:\Users\user01\AppData\Local\Continuum\anaconda3\lib\site-packages\pandas\core\generic.py", line 2531, in to_sql
    dtype=dtype, method=method)

  File "C:\Users\user01\AppData\Local\Continuum\anaconda3\lib\site-packages\pandas\io\sql.py", line 460, in to_sql
    chunksize=chunksize, dtype=dtype, method=method)

  File "C:\Users\user01\AppData\Local\Continuum\anaconda3\lib\site-packages\pandas\io\sql.py", line 1547, in to_sql
    table.insert(chunksize, method)

  File "C:\Users\user01\AppData\Local\Continuum\anaconda3\lib\site-packages\pandas\io\sql.py", line 686, in insert
    exec_insert(conn, keys, chunk_iter)

  File "C:\Users\user01\AppData\Local\Continuum\anaconda3\lib\site-packages\pandas\io\sql.py", line 609, in _execute_insert_multi
    conn.execute(self.table.insert(data))

TypeError: insert() takes exactly 2 arguments (1 given)

لماذا يحدث هذا؟ أنا أستخدم Python 3.7.3 (Anaconda) و pandas 0.24.2 و sqlite3 2.6.0.

شكرا جزيلا لك مقدما!

jconstanzo هل يمكنك فتح هذا كقضية جديدة؟
وإذا أمكن ، هل يمكنك محاولة تقديم مثال قابل للتكرار؟ (على سبيل المثال ، نموذج صغير لإطار البيانات يمكن أن يُظهر المشكلة)

jconstanzo وجود نفس المشكلة هنا. يبدو أن استخدام method='multi' (في حالتي ، بالاشتراك مع chunksize ) يؤدي إلى حدوث هذا الخطأ عند محاولة الإدراج في قاعدة بيانات SQLite.

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

انا اسف للتاخير. لقد فتحت للتو مشكلة لهذه المشكلة: https://github.com/pandas-dev/pandas/issues/29921

هل كانت هذه الصفحة مفيدة؟
0 / 5 - 0 التقييمات