tags:

views:

121

answers:

6

Take a look at the MySQL table below called "Articles":

+----+-----------+---------+------------------------+--------------------------+
| id | articleId | version | title                  | content                  |
+----+-----------+---------+------------------------+--------------------------+
|  1 |         1 | 0.0     | ArticleNo.1 title v0.0 | ArticleNo.1 content v0.0 |
|  2 |         1 | 1.0     | ArticleNo.1 title v1.0 | ArticleNo.1 content v1.0 |
|  3 |         1 | 1.5     | ArticleNo.1 title v1.5 | ArticleNo.1 content v1.5 |
|  4 |         1 | 2.0     | ArticleNo.1 title v2.0 | ArticleNo.1 content v2.0 |
|  5 |         2 | 1.0     | ArticleNo.2 title v1.0 | ArticleNo.2 content v1.0 |
|  6 |         2 | 2.0     | ArticleNo.2 title v2.0 | ArticleNo.2 content v2.0 |
+----+-----------+---------+------------------------+--------------------------+

Im trying to come up with a query to return Articles.id where Articles.version is the maximum number.

The actual Articles table contains over 10,000 entries.

So in this example I ONLY want Articles.id 4 and 6 to be returned. Ive been looking at keyword distinct and function max() but cant seem to nail it.

Any suggestions appreciated...

+2  A: 

You can use a subquery here:

select
    a.id
from
    articles a
where
    a.version = (select max(version) from articles)

Since this isn't a correlated subquery, it will be pretty fast (as fast or faster than a join), so you don't need to worry about that. But it'll return all of the values that have the max articleid.

Of course, I'd recommend that you throw an index on articleid (assuming id is already clustered). That will help it return even faster.

As noted in the comments, if you want to get the max version for each articleid, you can do the following:

select
    a.id
from
    articles a
    inner join (select articleid, max(version) 
                from articles group by articleid) as b on
        a.articleid = b.articleid
        and a.version = b.version

This will create a join out of that subquery and typically be much faster than a correlated subquery that selects the max for each articleid.

Eric
This isn't right - this selects the maximum article ID is max, not article version.
Tom Woolfrey
@Tom: Yeah, and now I fixed it, so it is right.
Eric
Note that this will only select articles that have the *same* version. If you want to fetch the latest version of *each* article, have a look at my answer below.
soulmerge
No, that will fail as soon as one article has a different max than another article. In effect, it looks for articles with version = 2, not articles with version = max version *for the article*.
T.J. Crowder
That was what I meant: It select all articles with the maximum version number (2 in this case).
soulmerge
@soulmerge: Comment overlap, the "no" was directed at Eric, not you. Sorry 'bout that.
T.J. Crowder
@Eric: Your latest edit still doesn't work (on MySQL, anyway), doesn't like the 'b' alias for the subquery.
T.J. Crowder
@TJ: Don't have access to MySQL. Is `as b` the valid syntax?
Eric
@TJ: Appears that is syntax: http://dev.mysql.com/doc/refman/5.1/en/unnamed-views.html --though it states in 5.1 that a subquery w/o `as` should work. Regardless, changed up in the answer.
Eric
@Eric: No, "as b" didn't work either. I just posted a working solution with a link to the MySQL manual page I stole it from. :-) I don't know why your syntax didn't work, as it's nearly identical to the first example on that page...which didn't work. One of their subsequent examples worked, though.
T.J. Crowder
@TJ: What version of MySQL are you using? It strikes me as odd that the documentation in more than one place says that is the correct syntax, but you're saying that it's not.
Eric
@Eric: Me too. v5.1.30. But I like their other solution better anyway.
T.J. Crowder
A: 

SELECT Articles.id FROM Articles WHERE Articles.version IN (SELECT MAX(Articles.version) FROM Articles)

Tom Woolfrey
A: 

maybe something like:

select * from articles where articleid in (select max(articleversion) from articles)

Dan
A: 

You can do

SELECT articles.id
  FROM articles a
 WHERE NOT EXISTS(SELECT * FROM articles a2 WHERE a2.version > a.version)

I don't know MySQL will perform with it, but Oracle and SQL server are fine with it.

erikkallen
+2  A: 

You need a sub query here:

SELECT a.id, a.version
FROM articles a
WHERE a.version = (
    SELECT MAX(version)
    FROM articles b
    WHERE b.articleId = a.articleId
)
soulmerge
This returns all the latest versions of articles only, discarding the previous/older versions. Nice queries lads thanks a lot!
DJDonaL3000
Just for the record: T.J. Crowder pointed out in his answer, that a sub-query is not mandatory: it can be done with a LEFT JOIN, too.
soulmerge
+3  A: 

From the MySQL manual, this does the trick:

SELECT a1.id
FROM Articles a1
LEFT JOIN Articles a2 ON a1.articleId = a2.articleId AND a1.version < a2.version
WHERE a2.articleId IS NULL;

See the link for rationale.

T.J. Crowder
Also be aware that subqueries in the WHERE clause in MySQL may get executed for every row in the result, so this is probably the best performing answer.
Richard Pistole