Sort string as number in sql server

前端 未结 9 1505
不知归路
不知归路 2020-12-20 16:41

I have a column that contains data like this. dashes indicate multi copies of the same invoice and these have to be sorted in ascending order

790711
790109-1         


        
9条回答
  •  陌清茗
    陌清茗 (楼主)
    2020-12-20 17:47

    Judicious use of REVERSE, CHARINDEX, and SUBSTRING, can get us what we want. I have used hopefully-explanatory columns names in my code below to illustrate what's going on.

    Set up sample data:

    DECLARE @Invoice TABLE (
        InvoiceNumber nvarchar(10)
    );
    
    INSERT @Invoice VALUES
    ('790711')
    ,('790709-1')
    ,('790709-11')
    ,('790709-21')
    ,('790709-212')
    ,('790709-2')
    
    SELECT * FROM @Invoice
    

    Sample data:

    InvoiceNumber
    -------------
    790711
    790709-1
    790709-11
    790709-21
    790709-212
    790709-2
    

    And here's the code. I have a nagging feeling the final expressions could be simplified.

    SELECT 
        InvoiceNumber
        ,REVERSE(InvoiceNumber) 
            AS Reversed
        ,CHARINDEX('-',REVERSE(InvoiceNumber)) 
            AS HyphenIndexWithinReversed
        ,SUBSTRING(REVERSE(InvoiceNumber),1+CHARINDEX('-',REVERSE(InvoiceNumber)),LEN(InvoiceNumber)) 
            AS ReversedWithoutAffix
        ,SUBSTRING(InvoiceNumber,1+LEN(SUBSTRING(REVERSE(InvoiceNumber),1+CHARINDEX('-',REVERSE(InvoiceNumber)),LEN(InvoiceNumber))),LEN(InvoiceNumber)) 
            AS AffixIncludingHyphen
        ,SUBSTRING(InvoiceNumber,2+LEN(SUBSTRING(REVERSE(InvoiceNumber),1+CHARINDEX('-',REVERSE(InvoiceNumber)),LEN(InvoiceNumber))),LEN(InvoiceNumber)) 
            AS AffixExcludingHyphen
        ,CAST(
            SUBSTRING(InvoiceNumber,2+LEN(SUBSTRING(REVERSE(InvoiceNumber),1+CHARINDEX('-',REVERSE(InvoiceNumber)),LEN(InvoiceNumber))),LEN(InvoiceNumber))
            AS int)  
            AS AffixAsInt
        ,REVERSE(SUBSTRING(REVERSE(InvoiceNumber),1+CHARINDEX('-',REVERSE(InvoiceNumber)),LEN(InvoiceNumber))) 
            AS WithoutAffix
    FROM @Invoice
    ORDER BY
        -- WithoutAffix
        REVERSE(SUBSTRING(REVERSE(InvoiceNumber),1+CHARINDEX('-',REVERSE(InvoiceNumber)),LEN(InvoiceNumber))) 
        -- AffixAsInt
        ,CAST(
            SUBSTRING(InvoiceNumber,2+LEN(SUBSTRING(REVERSE(InvoiceNumber),1+CHARINDEX('-',REVERSE(InvoiceNumber)),LEN(InvoiceNumber))),LEN(InvoiceNumber))
            AS int)
    

    Output:

    InvoiceNumber Reversed   HyphenIndexWithinReversed ReversedWithoutAffix AffixIncludingHyphen AffixExcludingHyphen AffixAsInt  WithoutAffix
    ------------- ---------- ------------------------- -------------------- -------------------- -------------------- ----------- ------------
    790709-1      1-907097   2                         907097               -1                   1                    1           790709
    790709-2      2-907097   2                         907097               -2                   2                    2           790709
    790709-11     11-907097  3                         907097               -11                  11                   11          790709
    790709-21     12-907097  3                         907097               -21                  21                   21          790709
    790709-212    212-907097 4                         907097               -212                 212                  212         790709
    790711        117097     0                         117097                                                         0           790711
    

    Note that all you actually need is the ORDER BY clause, the rest is just to show my working, which goes like this:

    • Reverse the string, find the hyphen, get the substring after the hyphen, reverse that part: This is the number without any affix
    • The length of (the number without any affix) tells us how many characters to drop from the start in order to get the affix including the hyphen. Drop an additional character to get just the numeric part, and convert this to int. Fortunately we get a break from SQL Server in that this conversion gives zero for an empty string.
    • Finally, having got these two pieces, we simple ORDER BY (the number without any affix) and then by (the numeric value of the affix). This is the final order we seek.

    The code would be more concise if SQL Server allowed us to say SUBSTRING(value, start) to get the string starting at that point, but it doesn't, so we have to say SUBSTRING(value, start, LEN(value)) a lot.

提交回复
热议问题