2011-11-04 6 views
5

Utilizzando SQLAlchemy Core (non ORM), sto cercando di INSERIRE più righe utilizzando sottoquery nei valori. Per MySQL, l'attuale SQL sarebbe simile a questa:Come inserire più valori con sottoquery utilizzando SQLAlchemy Core?

INSERT INTO widgets (name, type) VALUES 
('Melon', (SELECT type FROM widgetTypes WHERE type='Squidgy')), 
('Durian', (SELECT type FROM widgetTypes WHERE type='Spiky')) 

ma mi sembra solo di essere in grado di utilizzare le subquery quando si utilizza il metodo values() su una clausola insert() che permette soltanto che io faccia un inserto alla volta. Vorrei inserire più valori contemporaneamente passandoli tutti al metodo Connectionexecute() come elenco di parametri di collegamento, ma questo non sembra essere supportato.

È possibile fare ciò che voglio in una singola chiamata a execute()?

Ecco una dimostrazione autonoma. Nota: usa il motore sqlite che è doesn't support multiple inserts in the same way as MySQL, ma il codice SQLAlchemy fallisce ancora nello stesso modo della vera app MySQL.

from sqlalchemy import * 

if __name__ == "__main__": 
    # Construct database 
    metadata = MetaData() 
    widgetTypes = Table('widgetTypes', metadata, 
     Column('id', INTEGER(), primary_key=True), 
     Column('type', VARCHAR(), nullable=False), 
    ) 
    widgets = Table('widgets', metadata, 
     Column('id', INTEGER(), primary_key=True), 
     Column('name', VARCHAR(), nullable=False), 
     Column('type', INTEGER(), nullable=False), 
     ForeignKeyConstraint(['type'], ['widgetTypes.id']), 
    ) 
    engine = create_engine("sqlite://") 
    metadata.create_all(engine) 

    # Connect and populate db for testing 
    conn = engine.connect() 
    conn.execute(widgetTypes.insert(), [ 
     {'type': 'Spiky'}, 
     {'type': 'Squidgy'}, 
    ]) 

    # Some select queries for later use. 
    select_squidgy_id = select([widgetTypes.c.id]).where(
     widgetTypes.c['type']=='Squidgy' 
    ).limit(1) 
    select_spiky_id = select([widgetTypes.c.id]).where(
     widgetTypes.c['type']=='Squidgy' 
    ).limit(1) 

    # One at a time works via values() 
    conn.execute(widgets.insert().values(
     {'name': 'Tomato', 'type': select_squidgy_id}, 
    )) 

    # And multiple values work if we avoid subqueries 
    conn.execute(
     widgets.insert(), 
     {'name': 'Melon', 'type': 2}, 
     {'name': 'Durian', 'type': 1}, 
    ) 

    # Check above inserts did actually work 
    print conn.execute(widgets.select()).fetchall() 

    # But attempting to insert many at once with subqueries does not work. 
    conn.execute(
     widgets.insert(), 
     {'name': 'Raspberry', 'type': select_squidgy_id}, 
     {'name': 'Lychee', 'type': select_spiky_id}, 
    ) 

Run e muore l'ultimo execute() chiamata con:

sqlalchemy.exc.InterfaceError: (InterfaceError) Error binding parameter 1 - probably unsupported type. u'INSERT INTO widgets (name, type) VALUES (?, ?)' (('Raspberry', <sqlalchemy.sql.expression.Select at 0x19f14d0; Select object>), ('Lychee', <sqlalchemy.sql.expression.Select at 0x19f1a50; Select object>))

+0

Ho la sensazione che tu stia sbagliando - attualmente vuoi eseguire una subquery per ogni singolo record. E poiché stai usando SQLAlchemy Core, significa che tutto l'SQL verrà eseguito esattamente come lo fornisci. – plaes

+0

@plaes yep questo è esattamente quello che voglio fare (eseguire una subquery per ogni singolo record), devi solo fidarti di me che ha più senso nella vera applicazione ':)'. Ma il problema è che SQLAlchemy ** non esegue ** esattamente ciò che fornisco, e si rifiuta di elaborare l'espressione 'Select'': (' – Day

risposta

4

Invece di fornire un'istruzione di selezione secondaria come valore di parametro, bisogna integrarlo in istruzione INSERT:

type_select = select([widgetTypes.c.id]).where(
     widgetTypes.c.type==bindparam('type_name')) 

insert = widgets.insert({'type': type_select}) 

conn.execute(insert, [ 
    {'name': 'Melon', 'type_name': 'Squidgy'}, 
    {'name': 'Lychee', 'type_name': 'Spiky'}, 
]) 
+0

Fantastic che funziona bene grazie. Mi ha salvato dal dover effettuare più round trip al database - Ora posso fare tutto in una singola query che salva un ** lotto ** di tempo quando il tuo database è dall'altra parte del mondo. – Day

+2

Cosa succede se 'type_select' ha due campi? –