How did I not know about this gem earlier? I've been resorting to executesql() for my heavy joins-within-joins queries because, well, I thought I had to. The select join syntax doesn't do nested joins directly and I'd never heard of nested_select.
Wow, what a great tool! I just make my sub-join its own select, make an alias for it (required to use it with "on") and then put it in another query. It is a thing of beauty. (Well, maybe I have low standards of beauty!) This result is the equivalent of a very complex executesql() query: def gettimes(self, mid, eid, stage): assert mid and eid and stage db = current.db dbt,dbc,dbs,dbei,dbts,dbti = db.times,db.club,db.swimmer,db.event_index,db.time_std,db.time_std_info cols = [dbt[c] for c in ['ord','rank','evt','heat','lane','age','stime','stage','id']] cols += [dbc[c] for c in ['code','name','id']] cols += [dbei[c] for c in ['course','sex','distance','stroke','lower_age','upper_age']] cols += [dbs[c] for c in ['id','first','last','mi','preferred']] subq = db(dbts.id>0).nested_select(dbts.ALL, join=dbti.on((dbti.id==dbts.id_info)&(dbti.id_lsc=='51')&(dbti.code=='Default'))) dbtt = subq.with_alias('dbtt') cols += [dbtt[c] for c in ['name','next','time']] q = (dbt.id_meet==mid)&(dbt.id_event==eid)&(dbt.stage==stage) rows = db(q).select(*cols,cacheable=True,processor=self.as_list, join=[dbei.on(dbei.id==dbt.id_event), dbc.on(dbc.id==dbt.id_club), dbs.on(dbs.id==dbt.id_swim)], left=dbtt.on ((dbtt.event_code==dbei.code2)&(dbt.stime>0) &(dbtt.lower_age<=dbt.age)&(dbtt.upper_age>=dbt.age) &(dbtt.next<dbt.stime)&(dbtt.time>=dbt.stime))) return rows To save creation of Rows I used the "processor" keyword and made a little processor that basically returns a list of nested dicts, just as the normal "as_list" would to to a set of Rows. But it does it without creating the Rows first, saving time and memory. @staticmethod def as_list(rows, fields, colnames, blob_decode=True, cacheable = False): from collections import defaultdict rtn = list() cols = [(f.tablename,f.name) for f in fields] for row in rows: d = defaultdict(dict) for r,v in zip(cols,row): d[c[0]][c[1]] = r rtn.append(d) return rtn I'm pleased to get really good performance and still keep the power and generality of DAL. This feature might deserve additional explanation in the book. -- Joe If you want to see the executesql version, hold your nose and peek below. I'm sure it is ghastly and could be done much better. There are two variants I was playing with, one that uses Rows and as_list and another which makes the list of nested dicts directly. def gettimes2(self, mid, eid, stage): db = current.db dbt, dbc, dbs, dbei, dbts, dbti = db.times, db.club, db.swimmer, db.event_index, db.time_std, db.time_std_info assert mid and eid and stage table_alias = {'times':'dbt','club':'dbc','swimmer':'dbs','event_index':'dbei','time_std':'dbtt'} colnames = ['times.ord','times.rank','times.evt','times.heat','times.lane','times.age','times.stime','times.stage','times.id', 'club.code','club.name','club.id', 'event_index.course','event_index.sex','event_index.distance','event_index.stroke', 'event_index.lower_age','event_index.upper_age', 'swimmer.id','swimmer.first','swimmer.last','swimmer.mi','swimmer.preferred', 'time_std.name','time_std.next','time_std.time'] split_colnames = [fld.split('.') for fld in colnames] rnames=['.'.join([table_alias[t],db[t][f]._rname]) for t,f in split_colnames] sqlvars = dict(eid=eid, mid=mid, stage=stage, fields=','.join(rnames)) stmt = """ SELECT %(fields)s FROM sstimes.times dbt JOIN ssdata.event_index dbei ON dbei.id=dbt.id_event JOIN ssdata.club dbc ON dbc.id=dbt.id_club JOIN sstimes.swimmer dbs ON dbs.id=dbt.id_swim LEFT JOIN ( SELECT dbts.*,dbti.id_lsc FROM ssdata.time_std dbts JOIN ssdata.time_std_info dbti ON dbti.id=dbts.id_info AND dbti.id_lsc=51 AND dbti.code='Default' ) dbtt ON ( dbtt.event_code=dbei.code2 AND dbt.stime>0 AND dbtt.lower_age<=dbt.age AND dbtt.upper_age>=dbt.age AND dbtt.next<dbt.stime AND dbtt.time>=dbt.stime ) WHERE dbt.id_meet=%(mid)s AND dbt.id_event=%(eid)s AND dbt.stage='%(stage)s' """ if False: fields = [db[t][f] for t,f in split_colnames] rtn = db.executesql(stmt%sqlvars, fields=fields).as_list() else: rows = db.executesql(stmt%sqlvars, colnames=colnames, as_dict=True) # vvv turn [name1.name2] into [name1][name2], not needed fields=columns from collections import defaultdict rtn = [] for r in rows: d = defaultdict(dict) for k,v in r.items(): t,f = k.split('.') d[t][f] = v rtn.append(d) # ^^^ end of conversion return rtn -- Resources: - http://web2py.com - http://web2py.com/book (Documentation) - http://github.com/web2py/web2py (Source code) - https://code.google.com/p/web2py/issues/list (Report Issues) --- You received this message because you are subscribed to the Google Groups "web2py-users" group. To unsubscribe from this group and stop receiving emails from it, send an email to web2py+unsubscr...@googlegroups.com. For more options, visit https://groups.google.com/d/optout.