SQL Server 2008 - 按数字大小顺序排序字符串

25

我在我的表格中有以下数值:

ABC
ABC1
ABC2
ABC3 and so on...

ABC11
ABC12
ABC13 and so on..

ABC20
ABC21
ABC22 and so on..

基本上我手头有一个字符串值(不总是ABC,任何字符串值),它可能跟着一个数字,也可能是没有数字的字符串。

当我执行 select * from table order by my column 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

如何实现这一点?


最后有多少个字符可以是数字? - Sateesh Pagolu
3
AB1和ABC1有什么区别?它们是先后顺序还是无序的?最好的选择是保持一个排序列,避免像这样的内容进行排序。否则,你很快就会得到一个相当复杂的顺序,这将严重拖慢你的查询速度。 - Allan S. Hansen
3
“ABC”实际上可以是任何长度的字符串吗?它还可以包含数字字符,例如“A1B2C1234”吗?如果可以,您希望如何对其进行排序? - Tim Schmelter
7个回答

18

(基于 @shenhengbin@EchO这个问题 的回答)

以下是我所谓的“干净的黑客”。假设您正在按列Col1排序:

ORDER BY LEN(Col1), Col1

这是一个hack,尽管我个人使用它会感到自豪。


4
非常聪明的技巧 - 我感到很荣幸能够使用它 ;-) - Nicolas

17
您可以使用如下PATINDEX()函数来完成此操作:
select * from Test 
order by CAST(SUBSTRING(Name + '0', PATINDEX('%[0-9]%', Name + '0'), LEN(Name + '0')) AS INT)

SQL Fiddle演示

如果字符串中间有数字,则需要创建一个小型用户定义函数从字符串中获取数字,并根据该数字对数据进行排序,如下所示:

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

我没有收到任何错误信息,但是它没有正确地排序... 我得到的值是以'B'或'C'开头的,而不是以'A'开头的。 - user2179092
我的期望结果应该是 AD_NDU AD2_NDU ADA2006 AGNT_DOC APFORMS ASCBilling AUTH_FORM AUTH_FORM2 BC_U600 BECLAIMS CDB CIW_ASC CIW_BCS CIW_NCSC CIW1 CIW2 CIW3 等等... - user2179092
仍然无法正确排序 =( AD_NDU AGNT_DOC APFORMS ASCBilling AUTH_FORM BECLAIMS CDB CIW_ASC CIW_BCS CIW_NCSC CLAIMS CUST_INQ4 DBILLMBR4 MCAIDUB4 CMS_405 CIW41 CMS_410 CMS_412 CIW48 CIW49 CIW5 CUST_INQ5 DBILLMBR5 CIW50 DENTAL_500 BC_U600 CIW61 CIW62 CIW63 CIW64 CIW65 CIW66 CIW7 CIW8 - user2179092
让我们在聊天中继续这个讨论:http://chat.stackoverflow.com/rooms/42166/discussion-between-upendra-chaudhari-and-user2179092 - Upendra Chaudhari
我仍然无法理解您实际想要什么,如果您想根据数字和字母对数据进行排序,只需在order by子句中按顺序保留函数后跟名称,如果您想首先优先考虑字母,则更改order by子句中列的顺序即可,很简单。 - Upendra Chaudhari
显示剩余8条评论

7
在order by语句中,如果值中包含任何数字,则在其前面添加足够的零以使所有字母数字值具有相同的长度。
SELECT ColName
FROM TableName
ORDER BY 
 CASE WHEN ColName like '%[0-9]%' 
 THEN Replicate('0', 100 - Len(ColName)) + ColName
 ELSE ColName  END

5
你可以移除前三个字符,然后将剩余的部分转换为int类型。
SELECT Value,
       Num=CAST(RIGHT(Value, LEN(Value) - 3) AS int)
FROM dbo.TableName
ORDER BY Num

Demo


3
好的解决方案,如果字符串始终具有长度为3,则问题中的措辞似乎并不暗示这种情况... - Josien
1
它只适用于固定的三个字符字符串,对于像“ABCD11”这样的字符串不起作用。 - Upendra Chaudhari
@UpendraChaudhari:是的,这就是Josien已经提到的。我忽略了字符串是任意的这一部分。 - Tim Schmelter

2

您可以根据此答案中的RemoveNonAlphaCharacters函数进行调整,以过滤除数字以外的所有内容,然后使用该函数进行ORDER BY


1
使用标准SQL,此查询演示了如何按字符串开头或结尾包含的数字进行排序。该查询还展示了如何查看字符串的“内部”部分以查看其中是否包含任何数字。如果需要进一步处理,则了解字符串中是否包含数字可能很有用。
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


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忽略空格,并允许剩余值作为int进行排序。

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

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


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接