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

我们两清 提交于 2019-12-10 11:17:14

问题


I have a column in SQL Server that contains data like this:

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

How do I display this in ascending order?


回答1:


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



回答2:


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.




回答3:


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)



回答4:


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


来源:https://stackoverflow.com/questions/30426016/how-to-sort-a-varchar-datatype-column-by-its-numbers-in-sql-server

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