2011-11-04 34 views
5

Al usar SQLAlchemy Core (no ORM), intento INTRODUCIR varias filas usando subconsultas en los valores. Para MySQL, el SQL real sería algo como esto:¿Cómo insertar valores múltiples con subconsulta con SQLAlchemy Core?

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

Pero sólo parece ser capaz de utilizar subconsultas cuando se utiliza el método de values() en una cláusula insert() que sólo me permite hacer una inserción en un momento. Me gustaría insertar varios valores a la vez pasándolos todos al método Connectionexecute() como una lista de parámetros de enlace, pero esto no parece ser compatible.

¿Es posible hacer lo que quiero en una sola llamada al execute()?

Aquí hay una demostración independiente. Tenga en cuenta que utiliza el motor sqlite que doesn't support multiple inserts in the same way as MySQL, pero el código SQLAlchemy sigue fallando de la misma manera que la aplicación MySQL real.

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}, 
    ) 

Ejecutar y muere en el último execute() llamada 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

Tengo la intuición de que lo estás haciendo mal: actualmente quieres ejecutar una subconsulta para cada registro. Y debido a que está utilizando SQLAlchemy Core, significa que todo el SQL se ejecutará exactamente como lo suministra. – plaes

+0

@plaes yep eso es exactamente lo que quiero hacer (ejecutar una subconsulta para cada registro), solo tendrá que confiar en mí que tiene más sentido en la aplicación real ':)'. Pero el problema es que SQLAlchemy ** no está ** ejecutando exactamente lo que yo proporciono, y se niega a procesar la expresión 'Seleccionar'': (' – Day

Respuesta

4

En lugar de proporcionar instrucción de subselección como valor del parámetro, hay que incrustarlo en instrucción 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

Fantástico que funciona de maravilla gracias. Me salvó de tener que hacer múltiples viajes de ida y vuelta a la base de datos: ahora puedo hacer todo en una sola consulta que ahorra un ** lote ** de tiempo cuando tu base de datos está en el otro lado del mundo. – Day

+2

¿Qué pasa si 'type_select' tiene dos campos? –

Cuestiones relacionadas