字母数字排序

时间:2015-04-16 13:35:59

标签: sql-server tsql sorting sql-server-2012 natural-sort

我需要快速帮助在SQL端对数据进行排序。我正在使用Sqlserver 2012(如果使用新功能给出答案,那就很好。)

我已将某些链接搜索为Sorting in alphanumericAlphanumeric string Sorting in Sqlserver - Code project。但是没有给出理想的结果。

我还有什么尝试:

CREATE TABLE dbo.Section
    (
           Section varchar(50) NULL
    )
    INSERT INTO dbo.Section (Section.Section) VALUES ('Campsit no.43')
    INSERT INTO dbo.Section (Section.Section) VALUES ('Campsit no.41')
    INSERT INTO dbo.Section (Section.Section) VALUES ('Campsite No. 11')
    INSERT INTO dbo.Section (Section.Section) VALUES ('Campsite No. 1')
    INSERT INTO dbo.Section (Section.Section) VALUES ('Campsite No. 12')
    INSERT INTO dbo.Section (Section.Section) VALUES ('Campsite No. 2')
    INSERT INTO dbo.Section (Section.Section) VALUES ('Campsite No. 3')
    INSERT INTO dbo.Section (Section.Section) VALUES ('Campsite No. 4')
    INSERT INTO dbo.Section (Section.Section) VALUES ('Campsite No. 40')
    INSERT INTO dbo.Section (Section.Section) VALUES ('Campsite No. 41')
    INSERT INTO dbo.Section (Section.Section) VALUES ('Campsite no.20')
    INSERT INTO dbo.Section (Section.Section) VALUES ('Campsite no.41')
    INSERT INTO dbo.Section (Section.Section) VALUES ('Cabin')
    INSERT INTO dbo.Section (Section.Section) VALUES ('Group Tent Campsite')
    INSERT INTO dbo.Section (Section.Section) VALUES ('Tent Campsite')
    INSERT INTO dbo.Section (Section.Section) VALUES ('test1')
    INSERT INTO dbo.Section (Section.Section) VALUES ('test2')
    INSERT INTO dbo.Section (Section.Section) VALUES ('test11')
    SELECT Section
    FROM dbo.Section
    --Show normal Sort
    SELECT Section
    FROM dbo.Section
    ORDER BY Section
    --Show AlphaNumberic Sort
    SELECT Section
    FROM dbo.Section
    ORDER BY LEFT(Section,PATINDEX('%[0-9]%',Section)), -- alphabetical sort
             CONVERT(varchar(50),SUBSTRING(Section,PATINDEX('%[0-9]%',Section),LEN(Section))) -- numerical sort
    --cleanup our work
    --DROP Table dbo.Section

现在我想要的是:如果相同的字符串在字母表中找到第一个排序,然后是数字(如果可能的话也考虑空格,或者你可以给出没有空间的结果,如露营地第41号和露营地第41号会给出相同的顺序)

Actual Result          Expected Result
Campsit no.41          Campsit no.41
Campsit no.43          Campsit no.43
Campsite No. 1         Campsite No. 1
Campsite No. 11        Campsite No. 2
Campsite No. 12        Campsite No. 3
Campsite No. 2         Campsite No. 4
Campsite No. 21        Campsite No. 11
Campsite No. 3         Campsite No. 12
Campsite No. 4         Campsite No. 21
Campsite No. 40        Campsite No. 40
Campsite No. 41        Campsite No. 41
Campsite no.20         Campsite no.20 --this will good to come here, if possible or if not, then remove space and set approriate
Campsite no.41         Campsite no.41 --this will good to come here, if possible or if not, then remove space and set approriate
Group Tent Campsite    Group Tent Campsite
Tent Campsite          Tent Campsite
test1                  test1
test11                 test2
test2                  test11

5 个答案:

答案 0 :(得分:2)

这里有一个提示:每当您遇到排序问题时,按项目的顺序添加到您的select子句。这将使您能够查看您要排序的内容实际上是您要排序的内容:

SELECT Section,
        CASE WHEN PATINDEX('%[0-9]%',Section) > 1 THEN
          LEFT(Section,PATINDEX('%[0-9]%',Section)-1)
        ELSE 
          Section
        END As alphabetical_sort, -- alphabetical sort
        CASE WHEN PATINDEX('%[0-9]%',Section) > 1 THEN
          CAST(SUBSTRING(Section,PATINDEX('%[0-9]%',Section),LEN(Section)) as float)
        ELSE
          NULL
        END As Numeric_Sort
FROM dbo.Section
ORDER BY alphabetical_sort, Numeric_Sort

在我正确排序之后,我所要做的就是将case语句移到order by子句中:

SELECT Section
FROM dbo.Section
ORDER BY 
    CASE WHEN PATINDEX('%[0-9]%',Section) > 1 THEN
        LEFT(Section,PATINDEX('%[0-9]%',Section)-1)
    ELSE 
        Section
    END , -- Alphabetical sort
    CASE WHEN PATINDEX('%[0-9]%',Section) > 1 THEN
        CAST(SUBSTRING(Section,PATINDEX('%[0-9]%',Section),LEN(Section)) as float)
    ELSE
        NULL
    END  -- Numeric sort

基本上,你有4个主要问题:

  • 您的字母排序表达式假定每一行都有数字。
  • 您的字母排序表达式包含数字和文字。
  • 您的数字排序表达式包含数字和字母值。
  • 由于第3条的原因,您无法将数字排序表达式转换为数字类型,这就是为什么您会得到一个字符串排序。

See this sql fiddle

答案 1 :(得分:1)

下面给出了你所追求的结果,但我怀疑它是万无一失的。我认为你很难找到一个无懈可击但仍然表现良好的解决方案。

第一部分是获取第一个数字(它出现在空格之后,以便Campsite no.41被区别对待Campsite no. 40),我已将其放入APPLY以使其更容易重新 - 使用结果。

下一个阶段是在第一个数字后面找到第一个非数字字符,即数字结束的地方,这样我们就可以使用子字符串提取完整数字,然后最后使用TRY_CONVERT(INT将这个提取到可分类的。

SELECT  s.Section, 
        TextPart = SUBSTRING(s.Section, 1, ISNULL(fn.FirstNumber, LEN(s.Section))),
        Number = CASE WHEN FirstNumber IS NULL THEN NULL
                    ELSE TRY_CONVERT(INT, SUBSTRING(s.section, fn.FirstNumber + 1, ISNULL(ln.LastNumber, LEN(s.Section)))) 
                END
FROM    dbo.Section AS s
    -- GET FIRST NUMBER (WHERE PRECEDING CHARACTER IS A SPACE
    CROSS APPLY (SELECT NULLIF(PATINDEX('% [0-9]%', s.section), 0)) AS fn (FirstNumber)

    -- GET FIRST NON NUMERIC CHARACTER AFTER FIRST NUMBER
    CROSS APPLY (SELECT NULLIF(PATINDEX('%[^0-9]%', SUBSTRING(s.section, fn.FirstNumber + 1, LEN(s.Section))), 0)) AS ln (LastNumber)
ORDER BY TextPart, Number;

n.b。您需要将select中的表达式移动到顺序而不是列别名,但我已将其保留为此格式,以便更清楚地显示正在进行的操作

我试图评论解决方案,但有一点点正在进行,因此对每一位的完整解释将非常困难。对不起,如果有什么不清楚的话


修改

抱歉,错过了从想要(test1,test11,test2)切换到(test1,test2,test11)的更新。这只是改变你的逻辑来寻找第一个字母,但是现在它是前一个字符不是fullstup(PATINDEX('%[^.][0-9]%', s.section))的地方,而不是之前的字符就像以前一样的空格(这确保了{{1} }在Campsite no.20)之后排序

Campsite no. 40

答案 2 :(得分:1)

在这里尝试一下。注意:Cabin在您的数据中,但不在您的预期结果中。此外,如果您想要使用空格更改某些内容,请告诉我们。

SELECT  Section,
        FormatSection
FROM dbo.Section
CROSS APPLY (SELECT CASE 
                        WHEN PATINDEX('%[0-9]%',Section) != 0 
                            THEN SUBSTRING(Section,0,PATINDEX('%[0-9]%',Section)) + FORMAT(CAST(SUBSTRING(Section,PATINDEX('%[0-9]%',Section),5) AS INT),'0#')
                        ELSE Section
                    END
            ) AS CA(FormatSection)
ORDER BY FormatSection

结果:

Section                                            FormatSection
-------------------------------------------------- ---------------------
Cabin                                              Cabin
Campsit no.41                                      Campsit no.41
Campsit no.43                                      Campsit no.43
Campsite No. 1                                     Campsite No. 01
Campsite No. 2                                     Campsite No. 02
Campsite No. 3                                     Campsite No. 03
Campsite No. 4                                     Campsite No. 04
Campsite No. 11                                    Campsite No. 11
Campsite No. 12                                    Campsite No. 12
Campsite No. 40                                    Campsite No. 40
Campsite No. 41                                    Campsite No. 41
Campsite no.20                                     Campsite no.20
Campsite no.41                                     Campsite no.41
Group Tent Campsite                                Group Tent Campsite
Tent Campsite                                      Tent Campsite
test1                                              test01
test2                                              test02
test11                                             test11

答案 3 :(得分:1)

我找到了以下替代方案。

创建此函数,然后在查询“ ORDER BY fnGetNumericFromString([columnNm])”中使用此函数。

CREATE FUNCTION fnGetNumericFromString (@InString VARCHAR(20), @OutStrType VARCHAR(3))
    RETURNS VarChar(20)
    AS
    BEGIN
    -- declare variables
    DECLARE @pos INT
    DECLARE @strLength INT
    DECLARE @NumericString VarChar(20)
    DECLARE @CharString VarChar(20)
    DECLARE @ReturnValue VarChar(20)
    -- set values
    SET @NumericString = ''
    SET @CharString = ''
    SET @pos= 1
    SET @strLength = LEN(@InString)
    SET @InString = UPPER(@InString)
    SET @OutStrType = UPPER(@OutStrType)

    --start looping
    WHILE @pos <= @strLength 
    BEGIN
    -- number codes are 48 to 57
    IF ASCII(SUBSTRING(@InString, @pos, 1))BETWEEN 48 AND 57
    SET @NumericString = @NumericString + SUBSTRING(@InString, @pos, 1)
    else
    SET @CharString = @CharString + SUBSTRING(@InString, @pos, 1)

    --increment to next character
    SET @pos = @pos + 1
    END

    IF @OutStrType = 'STR'
    SET @ReturnValue = @CharString
    ELSE
    SET @ReturnValue = @NumericString

    RETURN @ReturnValue
    END


select section from Section
order by dbo.fnGetNumericFromString(section, 'str'), CAST(dbo.fnGetNumericFromString(section, 'int') AS INT)

答案 4 :(得分:-1)

 SELECT *,
       ROW_NUMBER()OVER(ORDER BY CASE WHEN ISNUMERIC (ID)=1 THEN CONVERT(NUMERIC(20,2),SUBSTRING(Id, PATINDEX('%[0-9]%', Id), LEN(Id)))END DESC)Rn ---- numerical
FROM
(
SELECT '1'Id UNION ALL
SELECT '25.20' Id UNION ALL
SELECT 'A115' Id UNION ALL
SELECT '2541' Id UNION ALL
SELECT '571.50' Id UNION ALL
SELECT '67' Id UNION ALL
SELECT 'B48' Id UNION ALL
SELECT '500' Id UNION ALL
SELECT '147.54' Id UNION ALL
SELECT 'A-100' Id
)A
ORDER BY 
CASE WHEN ISNUMERIC (ID)=0                                /* alphabetical sort */ 
     THEN CASE WHEN PATINDEX('%[0-9]%', Id)=0
               THEN LEFT(Id,PATINDEX('%[0-9]%',Id))
               ELSE LEFT(Id,PATINDEX('%[0-9]%',Id)-1)
          END
END DESC