2013-12-09 12 views
7

Si prega di suggerire c'è modo di scrivere query multi-column in clausola usando SQLAlchemy?Come scrivere clausola multi-column in sqlalchemy

Ecco esempio di query reale:

SELECT url FROM pages WHERE (url_crc, url) IN ((2752937066, 'http://members.aye.net/~gharris/blog/'), (3799762538, 'http://www.coxandforkum.com/')); 

Ho una tabella che ha due colonne chiave primaria e sto sperando di evitare di aggiungere una chiave più giusta per essere usato come un indice.

PS Sto usando mysql DB.

Aggiornamento: Questa query verrà utilizzata per l'elaborazione in batch, quindi avrei bisogno di inserire alcune centinaia di coppie nella clausola in. Con l'approccio della clausola IN spero di conoscere il limite fisso di quante coppie posso inserire in una query. Come Oracle ha 1000 limite enum di default.

L'utilizzo della combinazione AND/OR può essere limitata dalla lunghezza della query nei caratteri. Quale sarebbe variabile e meno prevedibile.

risposta

0

ho finito per usare il test() soluzione basata: generato "(a, b) a ((: A1, B1), (: a2 ,: b2), ...) "con named bind vars e generazione del dizionario con i valori di bind vars.

params = {} 
for counter, r in enumerate(records): 
    a_param = "a%s" % counter 
    params[a_param] = r['a'] 
    b_param = "b%s" % counter 
    params[b_param] = r['b'] 
    pair_text = "(:%s,:%s)" % (a_param, b_param) 
    enum_pairs.append(pair_text) 
multicol_in_enumeration = ','.join(enum_pairs) 
multicol_in_clause = text(
    " (a,b) in (" + multicol_in_enumeration + ")") 
q = session.query(Table.id, Table.a, 
          Table.b).filter(multicol_in_clause).params(params) 

Un'altra opzione che ho pensato di usare upserts MySQL, ma questo renderebbe tutto incluso ancor meno portatile per l'altro motore db quindi utilizzando più colonne nella clausola.

Aggiornamento SQLAlchemy ha il costrutto sqlalchemy.sql.expression.tuple_(*clauses, **kw) che può essere utilizzato per lo stesso scopo. (Non ho ancora provato)

1

Non penso che questo sia attualmente possibile in sqlalchemy, e non tutti gli RDMBS supportano questo.
Si può sempre trasformare questo ad una condizione OR(AND...) però:

filter_rows = [ 
    (2752937066, 'http://members.aye.net/~gharris/blog/'), 
    (3799762538, 'http://www.coxandforkum.com/'), 
    ] 
qry = session.query(Page) 
qry = qry.filter(or_(*(and_(Page.url_crc == crc, Page.url == url) for crc, url in filter_rows))) 
print qry 

dovrebbe produrre qualcosa di simile (per SQLite):

SELECT pages.id AS pages_id, pages.url_crc AS pages_url_crc, pages.url AS pages_url 
FROM pages 
WHERE pages.url_crc = ? AND pages.url = ? OR pages.url_crc = ? AND pages.url = ? 
-- (2752937066L, 'http://members.aye.net/~gharris/blog/', 3799762538L, 'http://www.coxandforkum.com/') 

alternativa, è possibile combinare due colonne in una sola:

filter_rows = [ 
    (2752937066, 'http://members.aye.net/~gharris/blog/'), 
    (3799762538, 'http://www.coxandforkum.com/'), 
    ] 
qry = session.query(Page) 
qry = qry.filter((func.cast(Page.url_crc, String) + '|' + Page.url).in_(["{}|{}".format(*_frow) for _frow in filter_rows])) 
print qry 

che produce il qui di seguito (per SQLite), in modo da poter utilizzare IN:

SELECT pages.id AS pages_id, pages.url_crc AS pages_url_crc, pages.url AS pages_url 
FROM pages 
WHERE (CAST(pages.url_crc AS VARCHAR) || ? || pages.url) IN (?, ?) 
-- ('|', '2752937066|http://members.aye.net/~gharris/blog/', '3799762538|http://www.coxandforkum.com/') 
+0

E ' è un'idea fattibile. Ma non si adatta alle mie esigenze. Ho esteso la domanda con maggiori dettagli. – vvladymyrov

+0

Risposta aggiornata anche con la versione 'IN'. – van

+0

Grazie per l'aggiornamento. Ho pensato di combinare due colonne in una stringa, ma sarebbe stato lento, in quanto in questo caso non sarebbe stato utilizzato alcun indice. – vvladymyrov

7

Supponendo che avete il vostro modello definito in Page, ecco un esempio utilizzando tuple_:

keys = [ 
    (2752937066, 'http://members.aye.net/~gharris/blog/'), 
    (3799762538, 'http://www.coxandforkum.com/') 
] 

select([ 
    Page.url 
]).select_from(
    Page 
).where(
    tuple_(Page.url_crc, Page.url).in_(keys) 
) 

Oppure, utilizzando l'API di query:

session.query(Page.url).filter(tuple_(Page.url_crc, Page.url).in_(keys)) 
Problemi correlati