2015-06-16 12 views
10

Vorrei creare una tabella di MySQL con funzione di to_sql Panda, che ha una chiave primaria (di solito è tipo di bene di avere una chiave primaria in una tabella mysql) come modo:Python Pandas to_sql, come creare una tabella con una chiave primaria?

group_export.to_sql(con = db, name = config.table_group_export, if_exists = 'replace', flavor = 'mysql', index = False) 

ma questo crea una tabella senza alcuna chiave primaria, (o anche senza alcun indice).

La documentazione menziona il parametro indel_label che potrebbe essere utilizzato per creare un indice ma non indica alcuna opzione per le chiavi primarie.

Documentation

+3

@unutbu penso che il 'index = true' assicura solo l'indice viene scritto nella tabella e che è un indice in SQL, e non ancora una chiave primaria – joris

+0

Sì, l'indice utilizza solo il numero di riga come un indice che non è quello che voglio. –

+3

Per ora, non c'è ancora il supporto per specificare le chiavi primarie (è nella lista dei desideri delle caratteristiche). Possibile soluzione per creare prima la tabella, quindi utilizzare l'opzione 'aggiungi' in 'to_sql'. Per creare la tabella, 'pd.io.sql.get_schema' potrebbe essere utile per creare lo schema (che quindi può essere adattato/eseguito per creare la tabella) – joris

risposta

8

responsabilità: questa risposta è più sperimentale poi pratico, ma forse vale la pena menzionare.

ho trovato quella classe pandas.io.sql.SQLTable ha nominato argomento key e se si assegna il nome del campo, allora questo campo diventa la chiave primaria:

Purtroppo non si può semplicemente trasferire questo argomento da DataFrame.to_sql() funzione. Per utilizzarlo è necessario:

  1. creare pandas.io.SQLDatabase esempio

    engine = sa.create_engine('postgresql:///somedb') 
    pandas_sql = pd.io.sql.pandasSQL_builder(engine, schema=None, flavor=None) 
    
  2. definire la funzione analoga aperta a pandas.io.SQLDatabase.to_sql() ma con l'aggiunta di *kwargs argomento che viene passato pandas.io.SQLTable oggetto creato al suo interno (ho appena copiato originale Metodo to_sql() e aggiunto *kwargs):

    def to_sql_k(self, frame, name, if_exists='fail', index=True, 
          index_label=None, schema=None, chunksize=None, dtype=None, **kwargs): 
        if dtype is not None: 
         from sqlalchemy.types import to_instance, TypeEngine 
         for col, my_type in dtype.items(): 
          if not isinstance(to_instance(my_type), TypeEngine): 
           raise ValueError('The type of %s is not a SQLAlchemy ' 
               'type ' % col) 
    
        table = pd.io.sql.SQLTable(name, self, frame=frame, index=index, 
            if_exists=if_exists, index_label=index_label, 
            schema=schema, dtype=dtype, **kwargs) 
        table.create() 
        table.insert(chunksize) 
    
  3. chiamata di questa funzione con il SQLDatabase istanza e la dataframe si desidera salvare

    to_sql_k(pandas_sql, df2save, 'tmp', 
         index=True, index_label='id', keys='id', if_exists='replace') 
    

e otteniamo qualcosa come

CREATE TABLE public.tmp 
(
    id bigint NOT NULL DEFAULT nextval('tmp_id_seq'::regclass), 
... 
) 

nel database.

PS È possibile, ovviamente, le patch di scimmia DataFrame, io.SQLDatabase e io.to_sql() per utilizzare questa soluzione alternativa con praticità.

+1

Bello. Grazie. Alla fine però ho trovato più semplice fare semplicemente la tabella prima e aggiungerla ad essa. –

+1

Speravo anche che l'opzione index_label di to_sql potesse essere d'aiuto. –

+1

Ottima risposta, sfortunatamente non funziona con MySQL se la colonna chiave è un tipo di testo perché i panda non sembrano avere un modo per specificare la lunghezza della chiave. Viene visualizzato l'errore 1170, "Colonna BLOB/TEXT utilizzata nelle specifiche chiave senza una lunghezza chiave" – danio

0

automap_base da sqlalchemy.ext.automap (tableNamesDict è un dict con solo le tabelle Pandas):

metadata = MetaData() 
metadata.reflect(db.engine, only=tableNamesDict.values()) 
Base = automap_base(metadata=metadata) 
Base.prepare() 

Che avrebbe funzionato perfettamente, ad eccezione di un problema, automap richiede le tabelle per avere una chiave primaria. Ok, nessun problema, sono sicuro che Pandas to_sql ha un modo per indicare la chiave primaria ... no. Questo è dove ottiene un po 'hacky:

for df in dfs.keys(): 
    cols = dfs[df].columns 
    cols = [str(col) for col in cols if 'id' in col.lower()] 
    schema = pd.io.sql.get_schema(dfs[df],df, con=db.engine, keys=cols) 
    db.engine.execute('DROP TABLE ' + df + ';') 
    db.engine.execute(schema) 
    dfs[df].to_sql(df,con=db.engine, index=False, if_exists='append') 

ho iterare attraverso il dict di DataFrames, ottenere un elenco delle colonne da utilizzare per la chiave primaria (cioèquelli contenenti id), utilizzare get_schema per creare le tabelle vuote quindi aggiungere lo DataFrame alla tabella.

Ora che avete i modelli, è possibile assegnare un nome in modo esplicito e utilizzarli (cioè User = Base.classes.user) con session.query o creare un dict di tutte le classi con qualcosa di simile:

alchemyClassDict = {} 
for t in Base.classes.keys(): 
    alchemyClassDict[t] = Base.classes[t] 

E query con:

res = db.session.query(alchemyClassDict['user']).first() 
+2

[pd.io.sql.get_schema non è nell'interfaccia pubblica] (https://github.com/pydata/pandas/issues/ 9960), quindi non va bene fare affidamento su di esso. Anche il codice funzionerà solo se il dataframe non ha un indice. Altrimenti devi usare qualcosa come 'schema = pd.io.sql.get_schema (df.reset_index(), table_name, con = db.engine, keys = cols)' – danio

11

Basta aggiungere la chiave primaria dopo aver caricato la tabella con i panda.

group_export.to_sql(con=engine, name=example_table, if_exists='replace', 
        flavor='mysql', index=False) 

with engine.connect() as con: 
    con.execute('ALTER TABLE `example_table` ADD PRIMARY KEY (`ID_column`);') 
Problemi correlati