2015-11-20 8 views
5

Bir SQL Server veritabanına mümkün olduğunca çabuk girmeye çalıştığım 74 nispeten büyük Pandas DataFrames (Yaklaşık 34.600 satır ve 8 sütun) var. Bazı araştırmalar yaptıktan sonra, iyi bir ole pandas.to_sql işlevinin, SQL Server veritabanına bu kadar büyük ekler için iyi olmadığını öğrendim. Bu, benim aldığım ilk yaklaşımdı (çok yavaş - yaklaşık 4 dakikalık bir uygulama için yaklaşık bir saat tamamlandı) .SQL Server veritabanına Büyük Pandalar DataFrames yazın

ben yerine sqlalchemy en Core'u kullanmaya çalışıyorum: mySQL veritabanını kullanırken)

This article ve diğer birçok StackOverflow mesajları ancak bir barikat vurdum bana doğru yönde işaret yardımcı olmuştur Yukarıdaki bağlantıda açıklanan nedenlerden dolayı ORM. Yani, pandas.to_dict kullanarak bir sözlüğe dataframe dönüştürme ediyorum ve daha sonra bir execute() ve insert() yapıyor: O insert edilir

self._session_factory.engine.execute(
    TimeSeriesResultValues.__table__.insert(), 
    data) 
# 'data' is a list of dictionaries. 

sorun herhangi bir değer almıyor - bunlar boş parantez ve ben bir grup olarak görünür

(pyodbc.IntegretyError) ('23000', "[23000] [FreeTDS][SQL Server]Cannot 
insert the value NULL into the column... 

Ben geçirilen sözlüklerde listesinde değerleri vardır, bu nedenle değerler görünmüyor neden çözemiyorum: Bu hatayı olsun.

DÜZENLEME:

Burada kapalı gidiyorum örnek: Eğer SQLAlchemy aslında SQL Server için toplu alma uygulamıyor için bazı üzücü haberlerim var

def test_sqlalchemy_core(n=100000): 
    init_sqlalchemy() 
    t0 = time.time() 
    engine.execute(
     Customer.__table__.insert(), 
     [{"name": 'NAME ' + str(i)} for i in range(n)] 
    ) 
    print("SQLAlchemy Core: Total time for " + str(n) + 
     " records " + str(time.time() - t0) + " secs") 
+0

sadece bağlantı yaklaşık 4 dakikadır *: İşte daha önce kullandığınız bir komut dosyası, ancak hiçbir garanti değildir MSSQL'de MySQL'e göre daha mı yavaş? Hangi ODBC API kullanıyorsunuz? Veritabanı sunucusu yerel mi yoksa uzak mı? Geçici tablo içe aktarmayı göz önünde bulundurun ve ardından son tabloya geçin. – Parfait

+0

@Parfait: '' 'to_sql() işlevinin kullanılması, MySQL ile kabul edilebilir bir performans sağlar, ancak MSSQL ile değil. Pyodbc kullanıyorum. Veritabanı uzaktadır, bu nedenle CSV dosyalarına yazı yazmak ve daha sonra ham sql kodu ile toplu bir ekleme yapmak gerçekten bu durumda çalışmayacaktır. Ek olarak, kullanıcılar bunu yapmak için toplu yönetim ayrıcalıklarına ihtiyaç duyarlar. Bu, bu uygulamanın kullanıcıları için her zaman mümkün olmayabilir. – denvaar

+1

Odbc sürücüsünü atlayarak ve kesinlikle Python API'sini kullanın - [pmyssl] (http://www.pymssql.org/en/latest/) Ve MySQL ODBC API'sı? pymysql? Her ikisinde de aynı tablo yapısı ve veri tipleri var mı? Aynı sayıda kayıt var mı? Bunu gerçekten araştırın. Her ikisi de yüksek seviyeli kurumsal RDMS'lerdir ve geniş bir aralıkta olmamalıdır (4 dakika vs ~ 60 dakika). – Parfait

cevap

7

, bu kadar Aslında sadece to_sql'un yaptığı aynı yavaş bireysel INSERT ifadelerini gerçekleştirecek. En iyi bahsinizin, bcp komut satırı aracını kullanarak bir şeyler komut dosyası denemek olduğunu söyleyebilirim. MySQL veritabanı * ... bu yüzden `to_sql()` uygulanabilir bir çözüm kullanırken

from subprocess import check_output, call 
import pandas as pd 
import numpy as np 
import os 

pad = 0.1 
tablename = 'sandbox.max.pybcp_test' 
overwrite=True 
raise_exception = True 
server = 'P01' 
trusted_connection= True 
username=None 
password=None 
delimiter='|' 
df = pd.read_csv('D:/inputdata.csv', encoding='latin', error_bad_lines=False) 



def get_column_def_sql(col): 
    if col.dtype == object: 
     width = col.str.len().max() * (1+pad) 
     return '[{}] varchar({})'.format(col.name, int(width)) 
    elif np.issubdtype(col.dtype, float): 
     return'[{}] float'.format(col.name) 
    elif np.issubdtype(col.dtype, int): 
     return '[{}] int'.format(col.name) 
    else: 
     if raise_exception: 
     raise NotImplementedError('data type {} not implemented'.format(col.dtype)) 
     else: 
     print('Warning: cast column {} as varchar; data type {} not implemented'.format(col, col.dtype)) 
     width = col.str.len().max() * (1+pad) 
     return '[{}] varchar({})'.format(col.name, int(width)) 

def create_table(df, tablename, server, trusted_connection, username, password, pad):   
    if trusted_connection: 
     login_string = '-E' 
    else: 
     login_string = '-U {} -P {}'.format(username, password) 

    col_defs = [] 
    for col in df: 
     col_defs += [get_column_def_sql(df[col])] 

    query_string = 'CREATE TABLE {}\n({})\nGO\nQUIT'.format(tablename, ',\n'.join(col_defs))  
    if overwrite == True: 
     query_string = "IF OBJECT_ID('{}', 'U') IS NOT NULL DROP TABLE {};".format(tablename, tablename) + query_string 


    query_file = 'c:\\pybcp_tempqueryfile.sql' 
    with open (query_file,'w') as f: 
     f.write(query_string) 

    if trusted_connection: 
     login_string = '-E' 
    else: 
     login_string = '-U {} -P {}'.format(username, password) 

    o = call('sqlcmd -S {} {} -i {}'.format(server, login_string, query_file), shell=True) 
    if o != 0: 
     raise BaseException("Failed to create table") 
    # o = call('del {}'.format(query_file), shell=True) 


def call_bcp(df, tablename): 
    if trusted_connection: 
     login_string = '-T' 
    else: 
     login_string = '-U {} -P {}'.format(username, password) 
    temp_file = 'c:\\pybcp_tempqueryfile.csv' 

    #remove the delimiter and change the encoding of the data frame to latin so sql server can read it 
    df.loc[:,df.dtypes == object] = df.loc[:,df.dtypes == object].apply(lambda col: col.str.replace(delimiter,'').str.encode('latin')) 
    df.to_csv(temp_file, index = False, sep = '|', errors='ignore') 
    o = call('bcp sandbox.max.pybcp_test2 in c:\pybcp_tempqueryfile.csv -S "localhost" -T -t^| -r\n -c') 
+0

Yanıt için teşekkürler - Bir dosya oluşturmayla ilgili herhangi bir şeyin bu özel durum için çalışıp çalışmadığını bilmiyorum. – denvaar

+0

Niçin desteklemediğiyle ilgili daha fazla bilginiz var mı? – denvaar