Here is the SQL:
We’ll do this by creating a subquery which selects each user and the timestamp of their latest tweet. Then we can query the tweets table in the outer query and join on the user and timestamp combination from the subquery.
# we will be querying from the Tweet model directly in the outer query.
Latest = Tweet.alias()
latest_query = (Latest
.select(Latest.user, fn.MAX(Latest.timestamp).alias('max_ts'))
.group_by(Latest.user)
.alias('latest_query'))
# Our join predicate will ensure that we match tweets based on their
# timestamp *and* user_id.
predicate = ((Tweet.user == latest_query.c.user_id) &
# We put it all together, querying from tweet and joining on the subquery
query = (Tweet
.select(Tweet, User) # Select all columns from tweet and user.
.join(latest_query, on=predicate) # Join tweet -> subquery.
.join_from(Tweet, User)) # Join from tweet -> user.
There are a couple things you may not have seen before in the code we used to create the query in this section:
- We used to explicitly specify the join context. We wrote
.join_from(Tweet, User)
, which is equivalent to.switch(Tweet).join(User)
. - We referenced columns in the subquery using the magic
.c
attribute, for examplelatest_query.c.max_ts
. The.c
attribute is used to dynamically create column references. - Instead of passing individual fields to
Tweet.select()
, we passed theTweet
andUser
models. This is shorthand for selecting all fields on the given model.
In the previous section we joined on a subquery, but we could just as easily have used a common-table expression (CTE). We will repeat the same query as before, listing users and their latest tweets, but this time we will do it using a CTE.
SELECT user_id, MAX(timestamp) AS max_ts
GROUP BY user_id)
SELECT tweet.*, user.*
FROM tweet
INNER JOIN latest
ON ((latest.user_id = tweet.user_id) AND (latest.max_ts = tweet.timestamp))
INNER JOIN user
ON (tweet.user_id = user.id)
This example looks very similar to the previous example with the subquery:
We can iterate over the result-set, which consists of the latest tweets for each user:
>>> for tweet in query:
... print(tweet.user.username, '->', tweet.content)
...
huey -> purr
For more information about using CTEs, including information on writing recursive CTEs, see the section of the “Querying” document.