ok, I think I found the solution. Thought I'd post what I did and what I tried; partly because it might help someone else and partly because someone may have suggestions for a better way.
This is what worked:
my_function = func.my_function(...args...)
func_subquery = select(['id'], from_obj=[my_function])
results = session.query(Table1).join(Table2).filter(Table1.id.in_(func_subquery))
and results in a query that looks like this:
SELECT
table1.id, table1.col1, table1.col2... FROM table1 JOIN table2 ON table1.t2_id =
table2.id WHERE
table1.id IN( SELECT id FROM my_function(...args...) )
This differs somewhat from what I was initially thinking:
SELECT
table1.id, table1.col1, table1.col2... FROM (SELECT * FROM my_function(...args...)) as table1 JOIN table2 ON table1.t2_id =
table2.id
When I run EXPLAIN ANALYZE in PostgreSQL the IN() version seems to be slightly more efficient according to the planner (and real run times are more or less the same)
IN: "Nested Loop (cost=12.75..889.97 rows=35432 width=222) (actual time=42.200..42.209 rows=2 loops=1)"
JOIN: "Nested Loop (cost=0.25..4386.37 rows=1000 width=226) (actual time=41.052..41.061 rows=2 loops=1)"
-----------------------------------------------------------------------------
Things I tried on the path to getting my answer:
-----------------------------------------------------------------------------
my_function = func.my_function(...args...).label(Table1.__tablename__)
my_function_alias = aliased(Table1, alias=my_function, adapt_on_names=True)
but that failed miserably. Using the labeled function in a regular core select resulted in an AttributeError exception, so I think that might have been part of the problem:
>>> print select('*', from_obj=[my_function])
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "/Library/Python/2.7/site-packages/sqlalchemy/sql/expression.py", line 1790, in __str__
return unicode(self.compile()).encode('ascii', 'backslashreplace')
File "/Library/Python/2.7/site-packages/sqlalchemy/sql/expression.py", line 1778, in compile
return self._compiler(dialect, bind=bind, **kw)
File "/Library/Python/2.7/site-packages/sqlalchemy/sql/expression.py", line 1784, in _compiler
return dialect.statement_compiler(dialect, self, **kw)
File "/Library/Python/2.7/site-packages/sqlalchemy/sql/compiler.py", line 277, in __init__
engine.Compiled.__init__(self, dialect, statement, **kwargs)
File "/Library/Python/2.7/site-packages/sqlalchemy/engine/base.py", line 705, in __init__
self.string = self.process(self.statement)
File "/Library/Python/2.7/site-packages/sqlalchemy/engine/base.py", line 724, in process
return obj._compiler_dispatch(self, **kwargs)
File "/Library/Python/2.7/site-packages/sqlalchemy/sql/visitors.py", line 72, in _compiler_dispatch
return getter(visitor)(self, **kw)
File "/Library/Python/2.7/site-packages/sqlalchemy/sql/compiler.py", line 873, in visit_select
froms = select._get_display_froms(existingfroms)
File "/Library/Python/2.7/site-packages/sqlalchemy/sql/expression.py", line 4807, in _get_display_froms
toremove = set(itertools.chain(*[f._hide_froms for f in froms]))
AttributeError: '_Label' object has no attribute '_hide_froms'
Using the aliased function would include the original table as well as the aliased functioned, and without a join condition it would just do a cartesian product:
>>> results = session.query(my_function_alias).first()
SELECT
table1.id, table1.col1, table1.col2... FROM table1, (SELECT * FROM my_function(...args...))
So that didn't work either. After doing this I realized that if I have to include the table and the function sub-select I might as well attempt it as an IN(), and that is what brought me to my final answer.
Thanks again for your suggestion Mike, it definitely put me on the right path to the solution.
On Thursday, September 20, 2012 7:10:52 PM UTC+2, Michael Bayer wrote: