views:

26

answers:

2

I have a simple query and am wondering if it could be more elegantly coded. The final solution has to be ansi-compliant.

I need to fetch the latest value from a table based on date and version. A sample would explain more clearly:

declare @t table (id int, due_date smalldatetime, version int, value nvarchar(10))

insert into @t select 3, '1/1/2010', 1, 'value 1'
insert into @t select 3, '1/1/2010', 2, 'value 2'
insert into @t select 3, '3/1/2010', 1, 'value 3'
insert into @t select 3, '3/1/2010', 2, 'value 4'
insert into @t select 3, '3/1/2010', 3, 'value 5'
insert into @t select 3, '3/1/2010', 4, 'value 6'
insert into @t select 3, '4/1/2010', 1, 'value 7'
insert into @t select 3, '4/1/2010', 2, 'value 8'
insert into @t select 3, '4/1/2010', 3, 'value 9'


select value from @t t
    inner join (select due_date, version=max(version) 
                from @t where due_date = (select max(due_date) from @t) group by due_date) maxes
    on t.due_date=maxes.due_date and t.version=maxes.version

So I would expect the output to be

value 9

which it is based on the above query.

I'm not particulary happy with this solution - any better ways to accomplish this?

Thanks.

+4  A: 

You could use:

  SELECT TOP 1 
         x.value
    FROM @t x
ORDER BY x.due_date DESC, x.version DESC

TOP is not ANSI, though. Another option would be to use ANSI analytical/rank/windowing functions:

SELECT x.value
  FROM (SELECT t.value,
               ROW_NUMBER() OVER (ORDER BY t.due_date DESC, t.version DESC) AS rank
          FROM @t t) x
 WHERE x.rank = 1

But this requires a database that supports the functionality - MySQL doesn't, PostgreSQL only started in v8.4...

OMG Ponies
Haha, this is so simple it didn't even cross my mind...
ck
That was incredibly quick (the responses, that is). I have never used the ranking functions in earnest - I think it's high time I learned... Thanks for the answers.EDIT: this is SQL Server 2005
Sean
@Sean: I'd recommend using TOP before ROW_NUMBER for this example, or consider either of Tom H's queries.
OMG Ponies
Hi OMG - any reason for that above comment? I have the code working and it makes sense to me. TOP (as you previously noted) is not ansi-compliant...
Sean
@Sean: Because as fast as ROW_NUMBER is, it will have to run through the entire table to attribute the rank value before performing the outer query. The less passes over the data, the more efficient the query.
OMG Ponies
Ah - I get you. I don't think this will be a major hurdle compared to how the original query stood. However, I did see a forum post in the last few minutes that said that ROW_NUMBER() was not ansi standard. Is this correct?
Sean
@Sean: It's my understanding ROW_NUMBER is ANSI - both Oracle 9i+ and SQL Server 2005+ support exactly the same syntax (including PostgreSQL 8.4+ iirc too). Even if it isn't ANSI, it is portable to databases with ranking functionality (IE: Not MySQL). Ask for a link proving ROW_NUMBER isn't - I'd like to see the info.
OMG Ponies
Here is a link to the article which states that it's not standard: http://www.ehow.com/how_6036858_use-row_number-function-tsql.htmlI can't find the date it was written though.
Sean
+1  A: 
SELECT
    value
FROM
    @t T1
LEFT OUTER JOIN @t T2 ON
    T2.id = T1.id AND
    (
        (T2.due_date > T1.due_date) OR
        (T2.due_date = T1.due_date AND T2.version > T1.version)
    )
WHERE
    T2.id IS NULL

or...

SELECT
    value
FROM
    @t T1
WHERE
    NOT EXISTS
    (
        SELECT
        FROM
            @t T2
        WHERE
            T2.id = T1.id AND
            (
                (T2.due_date > T1.due_date) OR
                (T2.due_date = T1.due_date AND T2.version > T1.version)
            )
    )
Tom H.