问题
I need to find a way to get the data with the highest versionNumber.
Here is my database design:
VERSIONNUMBER - varchar(15)
DOWNLOADPATH - varchar(100)
Lets say I have records like:
VERSIONNUMBER -------- DOWNLOADPATH
1.1.2 a.com
1.1.3 b.com
2.1.4 c.com
2.1.5 d.com
2.2.1 e.com
I need to get the record with the versionnumber 2.2.1. Need some help with the sql though :)
Thank you for any help
回答1:
Try this:
with a as
(
select * from (values
('1.1.2'),('1.1.3'),('2.1.4 '), ('2.1.5'), ('2.2.1') ) as b(c)
)
select c, PARSENAME(c,1),PARSENAME(c,2), PARSENAME(c,3)
from a
order by
convert(int,PARSENAME(c,3)),
convert(int,PARSENAME(c,2)),
convert(int,PARSENAME(c,1))
Inspired from: http://www.sql-server-helper.com/tips/sort-ip-address.aspx
with a as
(
select * from (values
('1.1.2'),('1.1.3'),('2.1.4 '), ('2.1.5'), ('2.2.1') ) as b(c)
),
x as
(
select c,
convert(int,PARSENAME(c,3)) * 100
+ convert(int,PARSENAME(c,2)) * 10
+ convert(int,PARSENAME(c,1)) * 1 as the_value
from a
)
select c from x where the_value = (select MAX(the_value) from x)
In software development, it is typical to find a minor version number that has two digits in it, the version's number don't have any bearing with number's value, thus version 1.12 is greater than 1.5; to compensate for that, you must pad the digits adequately:
-- Use this, the query above is not future-proof :-)
with a as
(
select * from (values
('2.1.4 '), ('2.1.12'), ('2.1.5'), ('2.2.1') ) as b(c)
),
x as
(
select c,
convert(int,PARSENAME(c,3)) * 100*100*100
+ convert(int,PARSENAME(c,2)) * 100*100
+ convert(int,PARSENAME(c,1)) * 100 as the_value
from a
)
select c, the_value from x
order by the_value
Output:
2.1.4 2010400
2.1.5 2010500
2.1.12 2011200
2.2.1 2020100
If you don't take that into consideration(as with the following query):
with a as
(
select * from (values
('2.1.4 '), ('2.1.12'), ('2.1.5'), ('2.2.1') ) as b(c)
),
x as
(
select c,
convert(int,PARSENAME(c,3)) * 100
+ convert(int,PARSENAME(c,2)) * 10
+ convert(int,PARSENAME(c,1)) * 1 as the_value
from a
)
select c, the_value from x
order by the_value;
-- KorsG's answer has a bug too
with a as
(
select * from (values
('2.1.4 '), ('2.1.12'), ('2.1.5'), ('2.2.1') ) as b(c)
),
x as
(
select c,
CAST(REPLACE(c, '.', '') AS int) as the_value
from a
)
select c, the_value from x
order by the_value
Those two queries will yield the same (incorrect) output:
c the_value
2.1.4 214
2.1.5 215
2.2.1 221
2.1.12 222
The 2.2.1 and 2.1.12's value overlapped. That also happens when you merely remove the dots and directly convert the resulting string to int. 2.1.12 become two thousand one hundred twelve, 2.2.1 become two hundred twenty one. 2.2.1 is greater than 2.1.12, not less than
回答2:
select top 1 DOWNLOADPATH
from YourTable
order by cast(parsename(VERSIONNUMBER, 3) as int) desc,
cast(parsename(VERSIONNUMBER, 2) as int) desc,
cast(parsename(VERSIONNUMBER, 1) as int) desc
回答3:
Alternatively, instead of multiplying each digit group, you can use ranking:
with a as
(
select * from (values
('2.1.4 '), ('2.1.12'), ('2.1.5'), ('2.2.1')
) as b(c)
),
x as
(
select c,
Ranking = RANK() over(order by convert(int,PARSENAME(c,3)), convert(int,PARSENAME(c,2)), convert(int,PARSENAME(c,1)))
from a
)
select * from x
order by ranking
Yields:
c Ranking
2.1.4 1
2.1.5 2
2.1.12 3
2.2.1 4
Final query:
with a as
(
select * from (values
('2.1.4 '), ('2.1.12'), ('2.1.5'), ('2.2.1')
) as b(c)
),
x as
(
select c,
Ranking = RANK() over(order by convert(int,PARSENAME(c,3)), convert(int,PARSENAME(c,2)), convert(int,PARSENAME(c,1)))
from a
)
select *
from x
where Ranking = (select MAX(ranking) from x)
Output:
c Ranking
2.2.1 4
Another simple approach, sort descending then just get the first row:
with a as
(
select * from (values
('2.1.4 '), ('2.1.12'), ('2.1.5'), ('2.2.1') ) as b(c)
),
x as
(
select c,
Ranking = RANK()
over(order by
convert(int,PARSENAME(c,3)) desc,
convert(int,PARSENAME(c,2)) desc,
convert(int,PARSENAME(c,1)) desc)
from a
)
select *
from x
where Ranking = 1
回答4:
If you are on SQL Server 2008 you can lever the HIERARCHYID
datatype.
SELECT VersionNumber, DownloadPath
FROM (VALUES
('1.1.2','a.com'),
('1.1.3','b.com'),
('2.1.4','c.com'),
('2.1.5','d.com'),
('2.2.1','e.com')
) AS T(VersionNumber, DownloadPath)
ORDER BY CAST('/' + VersionNumber + '/' AS HIERARCHYID) DESC
回答5:
This will work, but it ain't pretty - I would definitely consider changing the way you store version numbers.
concat(
right(concat(repeat("0",5), substring_index(VERSIONNUMBER,".",1)),5),
right(concat(repeat("0",5), substring_index(substring_index(VERSIONNUMBER,".",2),".",-1)),5),
right(concat(repeat("0",5), substring_index(VERSIONNUMBER,".",-1)),5))
Basically it turns "1.24.937"
into "000010002400937"
, which then sorts properly as a string.
回答6:
Personally, I like @Mikael's version, but obviously that's not quite as portable to other RDBMSes...
How about this? Quick and dirty, and works for three-number versions (as given in your example.)
The hack here is to realise that "2.59", for example, is a valid number, just not a valid integer, so you only have to split the string in one place -- you want the major version number, and the rest. This is hideous, but when it sprang to mind, I figured I'd share it, as it is at least short and hideous.
SELECT
TOP 1 downloadpath
FROM
version_table
ORDER BY
CAST(LEFT(VERSIONNUMBER, CHARINDEX( '.', VERSIONNUMBER) - 1) AS INTEGER) DESC,
CAST(SUBSTRING(VERSIONNUMBER, CHARINDEX( '.', VERSIONNUMBER) + 1, 100) AS FLOAT) DESC
Of course, the real answer is to change your database design to either split out the version numbers or include a build number or something like that...
回答7:
SELECT downloadpath FROM TABLE
WHERE versionnumber = (SELECT MAX(VersionNumber) FROM TABLE)
There might be a prettier way to do it as well.
来源:https://stackoverflow.com/questions/6855588/split-a-string-and-return-greatest-in-mssql