Hey Michael, First of all, thanks for the prompt answer and the pointer to the right direction!
I fiddled around with this, fixed and packaged it up, documented it, and created something hopefully useful to others: https://github.com/makmanalp/sqlalchemy-crosstab-postgresql - I found the $$ operator which removes the need for the awkward crosstab_param. This is useful because I have query bits and pieces that I already compose as functions (eg: get_year = lambda col: cast(func.date_part('year', func.to_timestamp(col)), Integer) etc) and I don't want to rewrite those using crosstab_param again. - I support the second form of crosstab that takes another query that defines possible columns, which IMHO is cleaner. - I auto-add the required order_bys Thanks again and hope this helps in the future. Cheers, ~mali On Fri, Aug 24, 2012 at 3:58 PM, Michael Bayer <mike...@zzzcomputing.com>wrote: > > On Aug 24, 2012, at 1:52 PM, Mali Akmanalp wrote: > > Hi All, > > I've been struggling recently to get postgres crosstabs [0] to work with > SQLAlchemy. The interesting thing about these is that they require you to > pass in a *string of a query* into the function, rather than an actual SQL > expression, like so: > > select * from crosstab('select column, row, count(foo) as 'value' from >> derp'); > > > >> > The closest thing I could come up with to this was to use the subquery > function, doing: > > sq = session.query(column, row, value).subquery() > > > for the inner part and then placing that into the outer func.crosstab(). > But of course I don't want to manually turn the subquery into a string by > putting it into quotes myself since there probably are a billion edge cases > I could miss and there probably is a better way. > > > > this is a new (and extremely awkward, wow) kind of SQL compilation > arrangement. Two ways to do it: > > 1. just use a string. connection/session.execute("select * from ..."). > this syntax is totally specific to Postgresql in any case. It's unlikely > you need lots of fluency here as the construct is so awkward to start with. > > 2. create a custom construct; see the docs at > http://docs.sqlalchemy.org/en/rel_0_7/core/compiler.html. Here is a > prototype, keeping in mind I'm not deeply familiar with this construct, I'm > just showing some techniques you can use to have the construction of it be > fairly automated. I'm assuming that bound parameters are out of the > question, so I have a function "crosstab_param" here - you can also use > bindparam() if you want bound values in the embedded SELECT: > > from sqlalchemy.sql import ColumnElement, FromClause, column, > literal_column > from sqlalchemy.ext.compiler import compiles > > """ > SELECT * > FROM crosstab( > 'select rowid, attribute, value > from ct > where attribute = ''att2'' or attribute = ''att3'' > order by 1,2') > AS ct(row_name text, category_1 text, category_2 text, category_3 text); > > """ > > class crosstab(FromClause): > def __init__(self, name, stmt, columns): > self.name = name > self.stmt = stmt > self.columns = columns > > def _populate_column_collection(self): > self._columns.update( > column(name, type=type_) > for name, type_ in self.names > ) > > def crosstab_param(value): > # we can't use bound parameters in crosstab? > return literal_column("''%s''" % value) > > @compiles(crosstab) > def visit_element(element, compiler, **kw): > return """ > crosstab('%s order by 1, 2') AS %s(%s) > """ % ( > compiler.visit_select(element.stmt), > element.name, > ", ".join( > "%s %s" % (c.name, compiler.visit_typeclause(c)) > for c in element.c > ) > ) > > from sqlalchemy import Table, Column, MetaData, Text, or_, select > m = MetaData() > > ct = Table('ct', m, Column('rowid', Text), > Column('attribute', Text), > Column('value', Text)) > > stmt = select([ct]).where( > or_( > ct.c.attribute == crosstab_param('att2'), > ct.c.attribute == crosstab_param('att3') > ) > ) > print select(['*']).select_from(crosstab("ct", stmt, ct.c)) > > > > -- > You received this message because you are subscribed to the Google Groups > "sqlalchemy" group. > To post to this group, send email to sqlalchemy@googlegroups.com. > To unsubscribe from this group, send email to > sqlalchemy+unsubscr...@googlegroups.com. > For more options, visit this group at > http://groups.google.com/group/sqlalchemy?hl=en. > -- You received this message because you are subscribed to the Google Groups "sqlalchemy" group. To post to this group, send email to sqlalchemy@googlegroups.com. To unsubscribe from this group, send email to sqlalchemy+unsubscr...@googlegroups.com. For more options, visit this group at http://groups.google.com/group/sqlalchemy?hl=en.