views:

70

answers:

2

I have a table defined like this:

Column:  Version     Message
Type:    varchar(20) varchar(100)
----------------------------------
Row 1:    2.2.6       Message 1
Row 2:    2.2.7       Message 2
Row 3:    2.2.12      Message 3
Row 4:    2.3.9       Message 4
Row 5:    2.3.15      Message 5

I want to write a T-Sql query that will get message for the MAX version number, where the "Version" column represents a software version number. I.e., 2.2.12 is greater than 2.2.7, and 2.3.15 is greater than 2.3.9, etc. Unfortunately, I can't think of an easy way to do that without using CHARINDEX or some complicated other split-like logic. Running this query:

SELECT MAX(Version) FROM my_table

will yield the erroneous result:

2.3.9

When it should really be 2.3.15. Any bright ideas that don't get too complex?

+7  A: 

One solution would be to use a table-valued split function to split the versions into rows and then combine them back into columns so that you can do something like:

Select TOP 1 Major, Minor, Build
From ( ...derived crosstab query )
Order By Major Desc, Minor Desc, Build Desc

Actually, another way is to use the PARSENAME function which was meant to split object names:

Select TOP 1 Version
From Table
Order By Cast(Parsename( Z.Version , 3 ) As Int) Desc
    , Cast(Parsename( Z.Version , 2 ) As Int) Desc
    , Cast(Parsename( Z.Version , 1 ) As Int) Desc
Thomas
+1: OP needs to make a data model change for the desired functionality.
OMG Ponies
The Parsename idea is pretty wifty, but I have to agree with OMG--that table design will not permit the desired simple query.
Philip Kelley
+1 for the creative use of 3 part name parsing...
Remus Rusanu
Yep, I realize it gets super easy if my data model is different. Maintaining legacy code can be such a pain sometimes! However, I will definitely keep the "best solution" in mind if I get the opportunity to do some database refactoring on this app. In the mean time...I agree...nice use of Parsename!
Ogre Psalm33
@Thomas. Hmm, actually, after testing on "real" data, I realized your Order By is reversed. Add "3.0.1" to that data set and it fails. If you Order By 'Cast(Parsename(Z.Version, 3) As Int) Desc, Cast(Parsename( Z.Version , 2 ) As Int) Desc, Cast(Parsename( Z.Version , 1 ) As Int) Desc' instead, it works correctly.
Ogre Psalm33
@Ogre Psalm33 - That should not be the case. You want to order by the Major (1) Desc, Minor (2) Desc, Build (3) Desc. If you do it the way you suggested, (Build Desc, Minor Desc, Major Desc), the version `1.2.99` will appear first ahead of say `2.2.15`.
Thomas
@Thomas - Check out the Parsename MSDN page: http://msdn.microsoft.com/en-us/library/ms188006.aspx. The order of the numbers is backwards of what you'd expect. It didn't make sense to me at first, but believe me, I tested it on real data :-).
Ogre Psalm33
@Ogre Psalm33 - OIC. That's my bad. I also misread it. I'll adjust my post to accommodate.
Thomas
+1  A: 

Does it have to be efficient on a large table? I suggest you create an indexed persisted computed column that transform the version into a format that ranks correctly, and use the computed column in your queries. Otherwise you'll always scan end to end.

If the table is small, it doesn't matter. Then you can use a just-in-time ranking, using a split function, or (ab)using the parsename as Thomas suggested.

Remus Rusanu
Interesting idea, but unfortunately, my real data is fairly large.
Ogre Psalm33