Split a string and return greatest in mssql

北慕城南 提交于 2019-12-04 01:17:34

问题


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

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!