SQL Server 2008 - 按字符顺序排列数字

时间:2013-11-27 10:47:38

标签: sql-server

我的表中有以下值:

ABC
ABC1
ABC2
ABC3 and so on...

ABC11
ABC12
ABC13 and so on..

ABC20
ABC21
ABC22 and so on..

基本上我所拥有的是任何字符串值(不总是ABC,任何字符串值),可以跟随数字,也可以只是没有数字的字符串。

当我通过我的列asc从表顺序中选择*时,我得到以下结果:

ABC
ABC1
ABC11
ABC12
ABC13
ABC2
ABC20
ABC21
ABC22
ABC3
ABC31
ABC32

我需要用数字排序:

ABC
ABC1
ABC2
ABC3
ABC11
ABC12
ABC13
ABC20
ABC21
ABC22
ABC31
ABC32

如何实现这一目标?

7 个答案:

答案 0 :(得分:12)

您可以使用PATINDEX()函数执行此操作,如下所示:

select * from Test 
order by CAST(SUBSTRING(Name + '0', PATINDEX('%[0-9]%', Name + '0'), LEN(Name + '0')) AS INT)

<强> SQL Fiddle Demo

如果您在字符串中间有数字,那么您需要创建小的用户定义函数以从字符串中获取数字并根据该数字对数据进行排序,如下所示:

CREATE FUNCTION dbo.fnGetNumberFromString (@strInput VARCHAR(255)) 
RETURNS VARCHAR(255) 
AS 
BEGIN 
    DECLARE @intNumber int 
    SET @intNumber = PATINDEX('%[^0-9]%', @strInput)

    WHILE @intNumber > 0
    BEGIN 
        SET @strInput = STUFF(@strInput, @intNumber, 1, '')
        SET @intNumber = PATINDEX('%[^0-9]%', @strInput)
    END 

    RETURN ISNULL(@strInput,0) 
END 
GO

您可以按以下方式对数据进行排序:

select Name from Test order by dbo.fnGetNumberFromString(Name), Name

答案 1 :(得分:5)

您可以删除前三个字符,然后将其余字符转换为int

SELECT Value,
       Num=CAST(RIGHT(Value, LEN(Value) - 3) AS int)
FROM dbo.TableName
ORDER BY Num

Demo

答案 2 :(得分:2)

您可以调整this answer中的函数 RemoveNonAlphaCharacters 来过滤掉除数字之外的所有内容,然后使用该函数使用ORDER BY

答案 3 :(得分:2)

按顺序声明,在值包含任何数字时,前缀足够零以使所有字母数字值相同

x² or xⁿ

答案 4 :(得分:1)

(基于从@shenhengbin@EchOthis question的答案)

以下是我所说的“干净的hack”。假设您正在RUNAS列上订购:

Col1

这是一个hack,尽管我个人对此感到自豪。

答案 5 :(得分:0)

仅使用标准 SQL 此查询演示了如何 ODER BY 找到包含在字符串开头或结尾的数字。该查询还显示了如何查看字符串剩余的“内部”部分以查看其中是否包含任何数字。如果需要进一步处理,了解字符串中是否包含数字可能会很有用。

WITH stringNumberData AS
(   /* Build up Fake data with Numbers at the Beginning, End and Middle of the string */
    SELECT 1  AS uniqueKey, 'ABC'      AS NumberFromString UNION ALL
    SELECT 2  AS uniqueKey, 'ABC1'     AS NumberFromString UNION ALL
    SELECT 3  AS uniqueKey, 'ABC2'     AS NumberFromString UNION ALL
    SELECT 4  AS uniqueKey, 'ABC3'     AS NumberFromString UNION ALL
    SELECT 5  AS uniqueKey, 'ABC10'    AS NumberFromString UNION ALL
    SELECT 6  AS uniqueKey, 'ABC11'    AS NumberFromString UNION ALL
    SELECT 7  AS uniqueKey, 'ABC12'    AS NumberFromString UNION ALL
    SELECT 8  AS uniqueKey, 'ABC20'    AS NumberFromString UNION ALL
    SELECT 9  AS uniqueKey, 'ABC21'    AS NumberFromString UNION ALL
    SELECT 10 AS uniqueKey, 'ABC22'    AS NumberFromString UNION ALL
    SELECT 11 AS uniqueKey, 'ABC30'    AS NumberFromString UNION ALL
    SELECT 12 AS uniqueKey, 'ABC31'    AS NumberFromString UNION ALL
    SELECT 13 AS uniqueKey, 'ABC32'    AS NumberFromString UNION ALL
    SELECT 14 AS uniqueKey, '1ABC'     AS NumberFromString UNION ALL
    SELECT 15 AS uniqueKey, '2ABC'     AS NumberFromString UNION ALL
    SELECT 16 AS uniqueKey, '3ABC'     AS NumberFromString UNION ALL
    SELECT 17 AS uniqueKey, '10ABC'    AS NumberFromString UNION ALL
    SELECT 18 AS uniqueKey, '11BC'     AS NumberFromString UNION ALL
    SELECT 19 AS uniqueKey, '12ABC'    AS NumberFromString UNION ALL
    SELECT 20 AS uniqueKey, '10ABC18'  AS NumberFromString UNION ALL
    SELECT 21 AS uniqueKey, '11BC52'   AS NumberFromString UNION ALL
    SELECT 22 AS uniqueKey, '12ABC42'  AS NumberFromString UNION ALL
    SELECT 23 AS uniqueKey, 'A3BC18'   AS NumberFromString UNION ALL
    SELECT 24 AS uniqueKey, 'B3C52'    AS NumberFromString UNION ALL
    SELECT 25 AS uniqueKey, '12AB3C'   AS NumberFromString UNION ALL
    SELECT 26 AS uniqueKey, 'A3BC'     AS NumberFromString UNION ALL
    SELECT 27 AS uniqueKey, 'AB2C'     AS NumberFromString UNION ALL
    SELECT 28 AS uniqueKey, 'ABC85D'   AS NumberFromString

)   

SELECT d.uniqueKey, d.NumberFromString
    /* Extract numerical values contained on the LEFT of the String by finding the index of the first non number */
    , LEFT(d.NumberFromString, PATINDEX('%[^0-9]%', d.NumberFromString) -1) AS 'Left Numbers Extraction'

    /* Extract numerical data contained on the RIGHT of the String */
    , RIGHT(d.NumberFromString, PATINDEX('%[^0-9]%', REVERSE(d.NumberFromString)) - 1 ) AS 'Right Numbers Extraction'

    /* The below checks inside the Inner string to determine if numbers exists within it.  Could be used for further processing if further extraction is necessary */
    , PATINDEX('%[0-9]%',
        SUBSTRING(d.NumberFromString /*, Start Pos, Length to Extract) */
            , PATINDEX('%[^0-9]%', d.NumberFromString)  /* Start Pos is first left non number */
            /* The below obtains the length of the Inner String so it can be extracted */
            , LEN(d.NumberFromString) - ((PATINDEX('%[^0-9]%', d.NumberFromString) -1 )) -  (PATINDEX('%[^0-9]%', REVERSE(d.NumberFromString)) -1) /* (String Length) - (LEFT Numbers) - (RIGHT Numbers) */
      )) AS innerNumExists

    /* The two lines below tell us if there exists a number at the Beginning and/or End of the string */
    , PATINDEX('%[0-9]%',  LEFT(d.NumberFromString, 1)) AS leftNumExists
    , PATINDEX('%[0-9]%', RIGHT(d.NumberFromString, 1)) AS rightNumExists

    /* Locates and returns the index of the very first number located in the string from Left to Right */
    , PATINDEX('%[^0-9]%', d.NumberFromString) AS firstLeftNonNum_index

    /* Locates and returns the index of the very first number located in the string from Right to Left */
    , LEN(d.NumberFromString) - (PATINDEX('%[^0-9]%', REVERSE(d.NumberFromString)) -1 ) AS firstRightNonNum_index

    /* Get the length of the numbers existing from Right to Left up to the first non numeric character */   
    , PATINDEX('%[^0-9]%', REVERSE(d.NumberFromString)) -1 AS rightStringLen


FROM stringNumberData d
ORDER BY 
    /* Ordering first by numbers found on the LEFT of the string */
    CAST(LEFT(d.NumberFromString, PATINDEX('%[^0-9]%', d.NumberFromString) -1) AS INT )
    /* Ordering second by numbers found on the RIGHT of the string */
    , CAST(RIGHT(d.NumberFromString, PATINDEX('%[^0-9]%', REVERSE(d.NumberFromString)) - 1 ) AS INT )
;

Query Results

答案 6 :(得分:0)

这里有一个简单易懂的示例,适合使用 SQL Server 17+ 的用户。

DECLARE @Data table ( val varchar(10) );
INSERT INTO @Data VALUES
    ( 'ABC' ),( 'ABC1' ),( 'ABC11' ),( 'ABC12' ),( 'ABC13' ),( 'ABC2' ), ( 'B1C' ),
    ( 'ABC20' ),( 'ABC21' ),( 'ABC22' ),( 'ABC3' ),( 'ABC31' ),( 'ABC32' );

SELECT val FROM @Data AS d
CROSS APPLY (
    SELECT CAST ( TRANSLATE ( d.val, 'abcdefghijklmnopqrstuvwxyz', '                          ' ) AS int ) AS Num
) AS x
ORDER BY
    LEFT ( val, 1 ), Num;

退货

+-------+
|  val  |
+-------+
| ABC   |
| ABC1  |
| ABC2  |
| ABC3  |
| ABC11 |
| ABC12 |
| ABC13 |
| ABC20 |
| ABC21 |
| ABC22 |
| ABC31 |
| ABC32 |
| B1C   |
+-------+

SQL Server 的 TRANSLATE 需要三个参数:inputString, characters, translations

您的案例中的 inputString 是您的列名。

characters 是您要替换的值,在本例中是字母表。

translations 是要替换的值。此字符串的长度必须与 characters 的长度相等——因此是 26 个空格长的空字符串。

最后,使用 CAST 会忽略空格并允许将剩余的值作为整数进行排序。

您可以在此处阅读有关 TRANSLATE 的信息:

https://docs.microsoft.com/en-us/sql/t-sql/functions/translate-transact-sql?view=sql-server-ver15