How to sort a varchar datatype column by its numbers in SQL Server

▼魔方 西西 提交于 2019-12-06 07:50:58

This should work:

SELECT  name
FROM    ( SELECT    name, SUBSTRING(name, 1,
                              LEN(name) - PATINDEX('%[0-9]%', REVERSE(name)) + 1) n
          FROM      #tmp
        ) v
ORDER BY CAST(PARSENAME(n, 4) AS INT),
         CAST(PARSENAME(n, 3) AS INT),
         CAST(PARSENAME(n, 2) AS INT),
         CAST(PARSENAME(n, 1) AS INT)

Output:

name
1.1.1.QuestionText
1.1.1.Question
1.1.1(a).Questions
1.1.2.Questionswithtext
1.1.2(b).Text
2.1.1.Questions
2.2.2.QuestionText
10.1.1.Answers

If you want to order by text after version number then this will work:

SELECT  name
FROM    ( SELECT    name, SUBSTRING(name, 1,
                              LEN(name) - PATINDEX('%[0-9]%', REVERSE(name)) + 1) nv,
                          SUBSTRING(name, LEN(name) - PATINDEX('%[0-9]%', REVERSE(name)) + 2, 
                              LEN(name)) nt                            
          FROM      #tmp
        ) v
ORDER BY CAST(PARSENAME(nv, 4) AS INT),
         CAST(PARSENAME(nv, 3) AS INT),
         CAST(PARSENAME(nv, 2) AS INT),
         CAST(PARSENAME(nv, 1) AS INT),
         nt  

Output:

name
1.1.1(a).Questions
1.1.1.Question
1.1.1.QuestionText
1.1.2(b).Text
1.1.2.Questionswithtext
2.1.1.Questions
2.2.2.QuestionText
10.1.1.Answers
Gordon Linoff

You happen to have a four-part naming, so you can use parsename(). That results in something like this:

order by (case when isnumeric(arsename(col, 4)) = 1 then cast(parsename(col, 4) as int)
          end),
         (case when isnumeric(parsename(col, 3)) = 1 then cast(parsename(col, 3) as int)
          end),
         (case when isnumeric(parsename(col, 2)) = 1 then cast(parsename(col, 2) as int)
          end),
         (case when isnumeric(parsename(col, 1)) = 1 then cast(parsename(col, 1) as int)
          end),
         col

This isn't perfect, because you seem to have oddly placed parentheses. But it might be good enough for your purposes.

Try this:

        CREATE TABLE #TMP
        (
         name nvarchar(30)
        )

        INSERT INTO #TMP (name) VALUES ('1.1.1.QuestionText')
        INSERT INTO #TMP (name) VALUES ('1.1.1.Question')
        INSERT INTO #TMP (name) VALUES ('1.1.1(a).Questions')
        INSERT INTO #TMP (name) VALUES ('1.1.2.Questionswithtext')
        INSERT INTO #TMP (name) VALUES ('1.1.2(b).Text')
        INSERT INTO #TMP (name) VALUES ('10.1.1.Answers')
        INSERT INTO #TMP (name) VALUES ('2.1.1.Questions')
        INSERT INTO #TMP (name) VALUES ('2.2.2.QuestionText')

        select
        SUBSTRING(replace(name, '.',''),1,
                PATINDEX('%[^0-9]%',  
                    replace(name, '.','')
                ) -1
            ) as replaced, name from #TMP order by 
        CAST( 
            SUBSTRING(replace(name, '.',''),1,
                PATINDEX('%[^0-9]%',  
                    replace(name, '.','')
                ) -1
            )
        as int)

It works always when it starts from digits. It is ordered by this first digits only. If you want to order by digits first and rest of the text later you can change last query to (I put in the select the same like in order by to show ordered parts):

        select
        SUBSTRING(replace(name, '.',''),1,
                PATINDEX('%[^0-9]%',  
                    replace(name, '.','')
                ) -1
            ) as replacedDigit,
            SUBSTRING(replace(name, '.',''),
                PATINDEX('%[^0-9]%',  
                    replace(name, '.','')
                ), 100) as replacedText,

             name from #TMP order by 
        CAST( 
            SUBSTRING(replace(name, '.',''),1,
                PATINDEX('%[^0-9]%',  
                    replace(name, '.','')
                ) -1
            )
        as int), SUBSTRING(replace(name, '.',''),
                PATINDEX('%[^0-9]%',  
                    replace(name, '.','')
                ), 100)

Try this

SELECT  *
FROM t
ORDER BY 
        CASE PATINDEX('%[^0-9]%', PARSENAME(name, 4)) 
            WHEN 0 THEN RIGHT('0000' + PARSENAME(name, 4), 4) 
            WHEN 1 THEN PARSENAME(name, 1)
            ELSE RIGHT('0000' + SUBSTRING(PARSENAME(name, 2), 1, PATINDEX('%[^0-9]%', PARSENAME(name, 2)) - 1), 4) + SUBSTRING(PARSENAME(name, 2), PATINDEX('%[^0-9]%', PARSENAME(name, 2)), LEN(name))
        END
    ,   CASE PATINDEX('%[^0-9]%', PARSENAME(name, 3)) 
            WHEN 0 THEN RIGHT('0000' + PARSENAME(name, 3), 4) 
            WHEN 1 THEN PARSENAME(name, 1)
            ELSE RIGHT('0000' + SUBSTRING(PARSENAME(name, 2), 1, PATINDEX('%[^0-9]%', PARSENAME(name, 2)) - 1), 4) + SUBSTRING(PARSENAME(name, 2), PATINDEX('%[^0-9]%', PARSENAME(name, 2)), LEN(name))
        END
    ,   CASE PATINDEX('%[^0-9]%', PARSENAME(name, 2)) 
            WHEN 0 THEN RIGHT('0000' + PARSENAME(name, 2), 4) 
            WHEN 1 THEN PARSENAME(name, 1)
            ELSE RIGHT('0000' + SUBSTRING(PARSENAME(name, 2), 1, PATINDEX('%[^0-9]%', PARSENAME(name, 2)) - 1), 4) + SUBSTRING(PARSENAME(name, 2), PATINDEX('%[^0-9]%', PARSENAME(name, 2)), LEN(name))
        END
    ,   CASE PATINDEX('%[^0-9]%', PARSENAME(name, 1)) 
            WHEN 0 THEN RIGHT('0000' + PARSENAME(name, 1), 4) 
            WHEN 1 THEN PARSENAME(name, 1)
            ELSE RIGHT('0000' + SUBSTRING(PARSENAME(name, 2), 1, PATINDEX('%[^0-9]%', PARSENAME(name, 2)) - 1), 4) + SUBSTRING(PARSENAME(name, 2), PATINDEX('%[^0-9]%', PARSENAME(name, 2)), LEN(name))
        END

For this:

1.1.1.Question
1.1.1.QuestionText
1.1.1(a).Questions
1.1.2.Questionswithtext
1.1.2(b).Text
2.1.1.Questions
2.2.2.QuestionText
10.1.1.Answers
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!