2015-07-13 14 views

risposta

20

A causa della potenza di SQLAlchemy, lo sto utilizzando anche su un progetto. Il suo potere deriva dal modo orientato agli oggetti di "parlare" a un database invece di istruzioni SQL hardcoded che possono essere difficili da gestire. Per non parlare, è anche molto più veloce.

Per rispondere alla tua domanda senza mezzi termini, si! Memorizzare i dati da un CSV in un database usando SQLAlchemy è un gioco da ragazzi. Ecco un esempio di lavoro completo (io ho usato SQLAlchemy 1.0.6 e Python 2.7.6):

from numpy import genfromtxt 
from time import time 
from datetime import datetime 
from sqlalchemy import Column, Integer, Float, Date 
from sqlalchemy.ext.declarative import declarative_base 
from sqlalchemy import create_engine 
from sqlalchemy.orm import sessionmaker 

def Load_Data(file_name): 
    data = genfromtxt(file_name, delimiter=',', skip_header=1, converters={0: lambda s: str(s)}) 
    return data.tolist() 

Base = declarative_base() 

class Price_History(Base): 
    #Tell SQLAlchemy what the table name is and if there's any table-specific arguments it should know about 
    __tablename__ = 'Price_History' 
    __table_args__ = {'sqlite_autoincrement': True} 
    #tell SQLAlchemy the name of column and its attributes: 
    id = Column(Integer, primary_key=True, nullable=False) 
    date = Column(Date) 
    opn = Column(Float) 
    hi = Column(Float) 
    lo = Column(Float) 
    close = Column(Float) 
    vol = Column(Float) 

if __name__ == "__main__": 
    t = time() 

    #Create the database 
    engine = create_engine('sqlite:///csv_test.db') 
    Base.metadata.create_all(engine) 

    #Create the session 
    session = sessionmaker() 
    session.configure(bind=engine) 
    s = session() 

    try: 
     file_name = "t.csv" #sample CSV file used: http://www.google.com/finance/historical?q=NYSE%3AT&ei=W4ikVam8LYWjmAGjhoHACw&output=csv 
     data = Load_Data(file_name) 

     for i in data: 
      record = Price_History(**{ 
       'date' : datetime.strptime(i[0], '%d-%b-%y').date(), 
       'opn' : i[1], 
       'hi' : i[2], 
       'lo' : i[3], 
       'close' : i[4], 
       'vol' : i[5] 
      }) 
      s.add(record) #Add all the records 

     s.commit() #Attempt to commit all the records 
    except: 
     s.rollback() #Rollback the changes on error 
    finally: 
     s.close() #Close the connection 
    print "Time elapsed: " + str(time() - t) + " s." #0.091s 

(Nota: questo non è necessariamente il modo "migliore" per fare questo, ma credo che questo formato è molto leggibile per un principiante, è anche molto veloce: 0,091 per 251 record inseriti!)

Penso che se lo si analizzi riga per riga, vedrai che è un gioco da ragazzi. Si noti la mancanza di istruzioni SQL - evviva! Mi sono anche preso la libertà di usare numpy per caricare il contenuto CSV in due righe, ma se lo desideri puoi farlo senza di esso.

Se si volesse confrontare con il modo tradizionale di farlo, ecco un esempio completo per la lavorazione di riferimento:

import sqlite3 
import time 
from numpy import genfromtxt 

def dict_factory(cursor, row): 
    d = {} 
    for idx, col in enumerate(cursor.description): 
     d[col[0]] = row[idx] 
    return d 


def Create_DB(db):  
    #Create DB and format it as needed 
    with sqlite3.connect(db) as conn: 
     conn.row_factory = dict_factory 
     conn.text_factory = str 

     cursor = conn.cursor() 

     cursor.execute("CREATE TABLE [Price_History] ([id] INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL UNIQUE, [date] DATE, [opn] FLOAT, [hi] FLOAT, [lo] FLOAT, [close] FLOAT, [vol] INTEGER);") 


def Add_Record(db, data): 
    #Insert record into table 
    with sqlite3.connect(db) as conn: 
     conn.row_factory = dict_factory 
     conn.text_factory = str 

     cursor = conn.cursor() 

     cursor.execute("INSERT INTO Price_History({cols}) VALUES({vals});".format(cols = str(data.keys()).strip('[]'), 
        vals=str([data[i] for i in data]).strip('[]') 
        )) 


def Load_Data(file_name): 
    data = genfromtxt(file_name, delimiter=',', skiprows=1, converters={0: lambda s: str(s)}) 
    return data.tolist() 


if __name__ == "__main__": 
    t = time.time() 

    db = 'csv_test_sql.db' #Database filename 
    file_name = "t.csv" #sample CSV file used: http://www.google.com/finance/historical?q=NYSE%3AT&ei=W4ikVam8LYWjmAGjhoHACw&output=csv 

    data = Load_Data(file_name) #Get data from CSV 

    Create_DB(db) #Create DB 

    #For every record, format and insert to table 
    for i in data: 
     record = { 
       'date' : i[0], 
       'opn' : i[1], 
       'hi' : i[2], 
       'lo' : i[3], 
       'close' : i[4], 
       'vol' : i[5] 
      } 
     Add_Record(db, record) 

    print "Time elapsed: " + str(time.time() - t) + " s." #3.604s 

(Nota: anche nel modo "vecchio", questo non è affatto il il modo migliore per farlo, ma è molto leggibile e una traduzione "1-a-1" da SQLAlchemy way vs. "old" way.)

Si noti le istruzioni SQL: uno per creare la tabella, il altro per inserire record. Inoltre, si noti che è un po 'più ingombrante mantenere lunghe stringhe SQL rispetto a una semplice aggiunta di attributi di classe. Mi piace SQLAlchemy finora?

Come per la richiesta chiave esterna, ovviamente. SQLAlchemy ha il potere di fare anche questo. Ecco un esempio di come un attributo di classe sarà simile con un incarico chiave esterna (assumendo che la classe ForeignKey inoltre è stato importato dal modulo sqlalchemy):

class Asset_Analysis(Base): 
    #Tell SQLAlchemy what the table name is and if there's any table-specific arguments it should know about 
    __tablename__ = 'Asset_Analysis' 
    __table_args__ = {'sqlite_autoincrement': True} 
    #tell SQLAlchemy the name of column and its attributes: 
    id = Column(Integer, primary_key=True, nullable=False) 
    fid = Column(Integer, ForeignKey('Price_History.id')) 

che indica la colonna "FID" come chiave esterna per Colonna id di Price_History.

Spero che questo aiuti!

+0

Prenderò il vecchio modo con lo sql. – javadba

+0

Questo è un codice utile, ma sarebbe utile se il file di dati fosse incluso nell'esempio. Quindi sarebbe veramente autonomo. –

+0

Non ho verificato il motivo per cui ciò sta accadendo, ma 'genfromtxt' restituisce l'errore:' genfromtxt() ha ottenuto un argomento di parole chiave inaspettato 'skiprows''. Numpy è '1.12.1-3' (Debian 9.0). –

13

Nel caso in cui il CSV sia abbastanza grande, l'utilizzo di INSERTS è molto inefficace. Dovresti utilizzare un meccanismo di caricamento di massa, che differisce da base a base. Per esempio. in PostgreSQL dovresti usare il metodo "COPY FROM":

with open(csv_file_path, 'r') as f:  
    conn = create_engine('postgresql+psycopg2://...').raw_connection() 
    cursor = conn.cursor() 
    cmd = 'COPY tbl_name(col1, col2, col3) FROM STDIN WITH (FORMAT CSV, HEADER FALSE)' 
    cursor.copy_expert(cmd, f) 
    conn.commit() 
+0

Per qualcosa di serio, vorrai davvero usare 'copy_from' o' copy_expert' direttamente da psycopg. Questa soluzione rende possibile inserire milioni di righe contemporaneamente. –

+0

c'è un modo per ottenere questo risultato senza importare una grossa libreria? – Sajuuk

Problemi correlati