在sql server中将字符串排序为数字

时间:2013-05-01 10:16:18

标签: sql sorting sql-server-2008-r2 natural-sort

我有一个包含这样的数据的列。破折号表示同一发票的多份副本,这些副本必须按升序排序

790711
790109-1
790109-11
790109-2

我必须按此数字按递增顺序对其进行排序,但由于这是一个varchar字段,因此按字母顺序排序,如此

790109-1
790109-11
790109-2
790711

为了解决这个问题,我尝试用空替换 - (破折号),然后将其作为数字投射,然后对其进行排序

select cast(replace(invoiceid,'-','') as decimal) as invoiceSort...............order by invoiceSort asc

虽然这样更好并且像这样排序

            invoiceSort
790711      (790711)   <-----this is wrong now as it should come later than 790109
790109-1    (7901091)
790109-2    (7901092)
790109-11   (79010911)

有人建议我在 - (破折号)上拆分发票ID,并在2个分割部分上按顺序分开

像=====&GT; order by split1 asc,split2 asc (790109,1)

哪个会起作用我认为但是我将如何拆分列。

互联网上的各种拆分功能是那些返回表格的功能,而在这种情况下我需要一个标量功能。

还有其他方法可以使用吗?数据显示在网格视图中,并且网格视图默认情况下不支持在2列上进行排序(我可以实现它:))因此,如果有任何更简单的方法,我会非常好。

编辑:感谢所有答案。虽然每个答案都是正确的,但我选择的答案允许我将这些列合并到GridView排序中,并最小化sql查询的因子。

9 个答案:

答案 0 :(得分:4)

试试这个 -

<强>查询:

DECLARE @Invoice TABLE (InvoiceNumber VARCHAR(10))
INSERT @Invoice 
VALUES
      ('790711')
    , ('790709-1')
    , ('790709-21')
    , ('790709-11')
    , ('790709-211')
    , ('790709-2')

;WITH cte AS 
(
    SELECT 
          InvoiceNumber
        , lenght = LEN(InvoiceNumber)
        , delimeter = CHARINDEX('-', InvoiceNumber)
    FROM @Invoice
)
SELECT InvoiceNumber
FROM cte
CROSS JOIN (
    SELECT repl = MAX(lenght - delimeter)
    FROM cte
    WHERE delimeter != 0
) mx
ORDER BY 
      SUBSTRING(InvoiceNumber, 1, ISNULL(NULLIF(delimeter - 1, -1), lenght))
    , RIGHT(REPLICATE('0', repl) + SUBSTRING(InvoiceNumber, delimeter + 1, lenght), repl)

<强>输出:

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

答案 1 :(得分:3)

明智地使用REVERSECHARINDEXSUBSTRING,可以获得我们想要的效果。我在下面的代码中使用了希望解释的列名来说明发生了什么。

设置样本数据:

DECLARE @Invoice TABLE (
    InvoiceNumber nvarchar(10)
);

INSERT @Invoice VALUES
('790711')
,('790709-1')
,('790709-11')
,('790709-21')
,('790709-212')
,('790709-2')

SELECT * FROM @Invoice

示例数据:

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

这是代码。我有一种唠叨的感觉,最终的表达方式可以简化。

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)

输出:

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

请注意,您实际需要的只是ORDER BY子句,其余的只是为了显示我的工作,这是这样的:

  • 反转字符串,找到连字符,获取连字符后的子字符串,反转该部分:这是没有任何词缀的数字
  • (没有任何词缀的数字)的长度告诉我们从一开始就要删除多少个字符以获得包含连字符的词缀。删除一个额外的字符以获取数字部分,并将其转换为int。幸运的是,我们从SQL Server中获得了一个突破,因为这个转换为空字符串提供了零。
  • 最后,有了这两个部分,我们简单ORDER BY(没有任何词缀的数字),然后是(词缀的数值)。这是我们寻求的最终订单。

如果SQL Server允许我们说SUBSTRING(value, start)从该点开始获取字符串,那么代码会更简洁,但事实并非如此,所以我们不得不说SUBSTRING(value, start, LEN(value))。< / p>

答案 2 :(得分:3)

试试这个

SELECT invoiceid FROM Invoice
ORDER BY 
CASE WHEN PatIndex('%[-]%',invoiceid) > 0
      THEN LEFT(invoiceid,PatIndex('%[-]%',invoiceid)-1)
      ELSE invoiceid END * 1
,CASE WHEN PatIndex('%[-]%',REVERSE(invoiceid)) > 0
      THEN RIGHT(invoiceid,PatIndex('%[-]%',REVERSE(invoiceid))-1)
      ELSE NULL END * 1

<强> SQLFiddle Demo

以上查询使用两个案例陈述

  1. 对Invoiceid 790109-1的第一部分进行排序(例如:790709)
  2. 使用' - '790109-1(例如:1)分割后对Invoiceid的第二部分进行排序
  3. 详细了解请查看以下SQLfiddle

    <强> SQLFiddle Detailed Demo

    或使用'CHARINDEX'

    SELECT invoiceid FROM Invoice
    ORDER BY 
    CASE WHEN CHARINDEX('-', invoiceid) > 0
          THEN LEFT(invoiceid, CHARINDEX('-', invoiceid)-1)
          ELSE invoiceid END * 1
    ,CASE WHEN CHARINDEX('-', REVERSE(invoiceid)) > 0
          THEN RIGHT(invoiceid, CHARINDEX('-', REVERSE(invoiceid))-1)
          ELSE NULL END * 1
    

答案 3 :(得分:2)

每个部分分别订购是最简单可靠的方法,为什么要寻找其他方法?看看这个简单的查询。

select *
from Invoice
order by Convert(int, SUBSTRING(invoiceid, 0, CHARINDEX('-',invoiceid+'-'))) asc,
         Convert(int, SUBSTRING(invoiceid, CHARINDEX('-',invoiceid)+1, LEN(invoiceid)-CHARINDEX('-',invoiceid))) asc

答案 4 :(得分:2)

这里有很多好的答案,但我认为这个可能是最紧凑的有效订单条款:

SELECT *
FROM Invoice
ORDER BY LEFT(InvoiceId,CHARINDEX('-',InvoiceId+'-'))
         ,CAST(RIGHT(InvoiceId,CHARINDEX('-',REVERSE(InvoiceId)+'-'))AS INT)DESC

演示: - SQL Fiddle

注意,我在测试中添加了'790709'版本,因为这里列出的一些方法并没有将后缀版本视为小于后缀版本。

如果您的invoiceID长度不同,那么在' - '之前,您需要:

SELECT *
FROM Invoice
ORDER BY CAST(LEFT(list,CHARINDEX('-',list+'-')-1)AS INT)
         ,CAST(RIGHT(InvoiceId,CHARINDEX('-',REVERSE(InvoiceId)+'-'))AS INT)DESC

在破折号之前使用不同长度的演示:SQL Fiddle

答案 5 :(得分:1)

我的版本:

declare @Len int
select @Len = (select max (len (invoiceid) -  charindex ( '-', invoiceid))-1 from MyTable)

select 
invoiceid ,
cast (SUBSTRING (invoiceid ,1,charindex ( '-', invoiceid )-1) as int) * POWER (10,@Len) + 
cast (right(invoiceid, len (invoiceid) -  charindex ( '-', invoiceid)  ) as int )
from MyTable

您可以将其实现为表格的新列:

ALTER TABLE MyTable ADD COLUMN invoice_numeric_id int null
GO

declare @Len int
select @Len = (select max (len (invoiceid) -  charindex ( '-', invoiceid))-1 from MyTable)


UPDATE TABLE MyTable
SET  invoice_numeric_id = cast (SUBSTRING (invoiceid ,1,charindex ( '-', invoiceid )-1) as int) * POWER (10,@Len) + 
    cast (right(invoiceid, len (invoiceid) -  charindex ( '-', invoiceid)  ) as int )

答案 6 :(得分:1)

一种方法是将InvoiceId拆分为其部分,然后对部件进行排序。这里我使用派生表,但也可以使用CTE或临时表来完成。

select InvoiceId, InvoiceId1, InvoiceId2
from
(
    select
    InvoiceId,
    substring(InvoiceId, 0, charindex('-', InvoiceId, 0)) as InvoiceId1,
    substring(InvoiceId, charindex('-', InvoiceId, 0)+1, len(InvoiceId)) as InvoiceId2
    FROM Invoice
) tmp
order by
cast((case when len(InvoiceId1) > 0 then InvoiceId1 else InvoiceId2 end) as int),
cast((case when len(InvoiceId1) > 0 then InvoiceId2 else '0' end) as int)

在上文中,InvoiceId1InvoiceId2InvoiceId的组成部分。 外部 select包含这些部分,但仅用于演示目的 - 您无需在选择中执行此操作。

派生表(内部select)抓取InvoiceId以及组件部分。它的工作方式是:

  • InvoiceId中有短划线时,InvoiceId1将包含数字的第一部分,InvoiceId2将包含第二部分。
  • 如果没有短划线,InvoiceId1将为空,InvoiceId2将包含整个数字。

上面的第二个案例(没有破折号)不是最佳的,因为理想情况下InvoiceId1将包含数字,InvoiceId2将为空。为了使内部选择最佳地工作会降低选择的可读性。我选择了非最佳,更易读的方法,因为它足以允许排序。

这就是ORDER BY子句测试长度的原因 - 它需要处理上面两种情况。

SQL Fiddle

演示

答案 7 :(得分:1)

将排序分为两部分:

SQL Fiddle

MS SQL Server 2008架构设置

CREATE TABLE TestData
(
  data varchar(20)
)

INSERT TestData
SELECT '790711' as data
UNION
    SELECT '790109-1'
UNION
    SELECT '790109-11'
UNION 
    SELECT '790109-2'

查询1

SELECT *
FROM TestData
ORDER BY 
    FLOOR(CAST(REPLACE(data, '-', '.') AS FLOAT)),
    CASE WHEN CHARINDEX('-', data) > 0 
        THEN CAST(RIGHT(data, len(data) - CHARINDEX('-', data)) AS INT)
        ELSE 0 
    END

<强> Results

|      DATA |
-------------
|  790109-1 |
|  790109-2 |
| 790109-11 |
|    790711 |

答案 8 :(得分:0)

尝试:

select invoiceid  ... order by Convert(decimal(18, 2), REPLACE(invoiceid, '-', '.'))