First, tell me if the query I've written below is equivalent to what
you're trying to select. The basic idea is that the first nested
subquery which joins dkp_run to _last_run_date_by_character is not
needed. I'm pretty sure this is the case but you have the more
ingrained knowledge of the query plus some test data with which to
verify.
SELECT
dkp_character.id,
dkp_character.name,
dkp_run.id
FROM dkp_character
LEFT OUTER JOIN
(
SELECT
dkp_dkpattendance.character_id AS character_id,
MAX(dkp_run.guild_id) AS last_run_guild_id,
MAX(dkp_run.date) AS last_run_date
FROM dkp_dkpattendance
JOIN dkp_run
ON dkp_dkpattendance.run_id = dkp_run.id
GROUP BY dkp_dkpattendance.character_id
) AS last_run
ON dkp_character.id=last_run.character_id
LEFT OUTER JOIN dkp_run
ON
dkp_run.date=last_run.last_run_date AND
dkp_run.guild_id=last_run.last_run_guild_id
Next, we can think a bit about ways to issue this query from an ORM
perspective. The above query really doesn't seem to me like the
intended use case for relation() - you'd in theory be loading a set of
Character objects which each refer to a single Run object. But
there's really no direct "relation", in the foreign key sense, between
Character and Run. Which is why relation() is seeming like an awkward
fit. relation()'s primary use, although it can be stretched way
beyond this, is to satisfy direct foreign key relationships.
relation() can do it - and in fact your original idea of sticking
"secondary" in there is possibly a quick way to make this happen
(let's label this choice A). But what disturbs me about relying upon
that technique is, what if we needed to LEFT OUTER JOIN four levels
deep to get to the target table instead of two tables. That was the
intuition which told me "don't rely on secondary". We can
alternatively use relation() without relying upon "secondary", if we
took the intermediary table and placed it as a correlated subquery
within the ON clause of dkp_character joining to dkp_run (choice
B). Or we could LEFT OUTER JOIN arbitrarily deep by mapping each
association unit to a class...but this is likely overkill for the
current problem space (choice C).
While we do support the use case of a relation() joining on a
correlated subquery, it still seems awkward, which still suggests
relation() as a less than appropriate place for this. The advantage
relation() brings, when used in conjunction with the eager load
functionality, is that the extra join for the Run is emitted under a
wide variety of circumstances without the need to specify it
explicitly. If you didnt need the eager load, a @property which
issues the query upon access would do this much more cleanly, and if
you didn't need the implicit Run being added to Character in a wide
variety of situations, a straight Query which joins everything
explicitly would again do this much more cleanly.
You've already stated the JOIN is needed as opposed to a separate
query, so all that's left is the implicit join functionality which
would occur in all situations with just the usage of the eagerload()
option. If OTOH the "implicitness" of eagerload() isn't critical, I
really wouldn't use relation() to generate the join from dkp_character
to dk_run. This query does seem to be specific to one part of the
application, in which case I'd spell it out explicitly on an as-needed
basis.
As far as the Character and Run objects returned, whether or not you
use eagerload you still have the option to get back a Character with a
Run attached to it, which still would make some usage of relation()
(choice D), or you can instead get back 2-tuples each consisting of a
Character and a Run object. It is in fact a "named" tuple so you
could say "row.Character", "row.Run" (choice E).
My own choice would be the last option - the above SQL is rendered
using an explicit Query that does the right thing ( I can show you
many ways to do that part), and I'd organize the results as tuples
since the Character and Run objects are pretty separate from a domain
point of view.
So weigh out the pros and cons of using relation() with regards to
your application's needs, and I can show you how to do any of choice A-
E.
On Dec 29, 2008, at 4:06 PM, Alan Shields wrote:
>
> Alrighty, here's a pared down version of what I'm dealing with.
>
> guild_table = Table(
> 'dkp_guild', metadata,
> Column('id', Integer, primary_key=True),
> Column('name', Unicode(45), nullable=False),
> )
>
> character_table = Table(
> 'dkp_character', metadata,
> Column('id', Integer, primary_key=True),
> Column('guild_id', Integer, ForeignKey(guild_table.c.id),
> nullable=False),
> Column('name', Unicode(30)),
> )
>
> pool_table = Table(
> 'dkp_pool', metadata,
> Column('id', Integer, primary_key=True),
> Column('guild_id', Integer, ForeignKey(guild_table.c.id),
> nullable=False),
> )
>
> event_table = Table(
> 'dkp_event', metadata,
> Column('id', Integer, primary_key=True),
> Column('pool_id', Integer, ForeignKey(pool_table.c.id),
> nullable=False),
> )
>
> run_table = Table(
> 'dkp_run', metadata,
> Column('id', Integer, primary_key=True),
> Column('guild_id', Integer, ForeignKey(guild_table.c.id),
> nullable=False),
> Column('date', DateTime(timezone=True)),
> )
>
> dkpattendance_table = Table(
> 'dkp_dkpattendance', metadata,
> Column('id', Integer, primary_key=True),
> Column('character_id', Integer, ForeignKey(character_table.c.id),
> nullable=False),
> Column('run_id', Integer, ForeignKey(run_table.c.id),
> nullable=False),
> )
>
> character_table = Table(
> 'dkp_character', metadata,
> Column('id', Integer, primary_key=True),
> Column('guild_id', Integer, ForeignKey(guild_table.c.id),
> nullable=False),
> Column('name', Unicode(30)),
> )
>
> The SQL query I'm looking to run would be this:
>
> SELECT dkp_character.id, dkp_character.name, dkp_run.id
> FROM dkp_character
> LEFT OUTER JOIN (SELECT _last_run_date_by_character.character_id AS
> character_id, dkp_run.id AS run_id
> FROM (SELECT dkp_dkpattendance.character_id AS
> character_id, MAX(dkp_run.guild_id) AS last_run_guild_id,
> MAX(dkp_run.date) AS last_run_date
> FROM dkp_dkpattendance
> JOIN dkp_run ON dkp_dkpattendance.run_id =
> dkp_run.id
> GROUP BY dkp_dkpattendance.character_id) AS
> _last_run_date_by_character
> LEFT OUTER JOIN dkp_run ON (
> _last_run_date_by_character.last_run_date = dkp_run.date
> AND
> _last_run_date_by_character.last_run_guild_id = dkp_run.guild_id)
> ) AS _last_run_by_character ON dkp_character.id =
> _last_run_by_character.character_id
> LEFT OUTER JOIN dkp_run ON _last_run_by_character.run_id = dkp_run.id
> ;
>
> For those following along at home: use attendance records to find out
> which runs a character has attended, then grab the most recent ordered
> by date.
>
> I hope this is a bit more intelligble. To be honest, since this is a
> one-man project, a lot of times my really bad ideas will keep running
> for quite a while.
>
> Alan
>
> >
--~--~---------~--~----~------------~-------~--~----~
You received this message because you are subscribed to the Google Groups
"sqlalchemy" group.
To post to this group, send email to [email protected]
To unsubscribe from this group, send email to
[email protected]
For more options, visit this group at
http://groups.google.com/group/sqlalchemy?hl=en
-~----------~----~----~----~------~----~------~--~---