8

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

790711
790109-1
790109-11
790109-2

我必须按此数字按升序对其进行排序,但由于这是一个 varchar 字段,因此它按字母顺序排序,如下所示

790109-1
790109-11
790109-2
790711

为了解决这个问题,我尝试将 -(dash) 替换为空,然后将其转换为数字,然后对其进行排序

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 个拆分部分上订购

喜欢=====> order by split1 asc,split2 asc (790109,1)

我认为这会起作用,但我将如何拆分列。

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

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

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

4

9 回答 9

4

明智地使用REVERSE,CHARINDEXSUBSTRING, 可以得到我们想要的。我在下面的代码中使用了希望解释性的列名称来说明发生了什么。

设置样本数据:

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))很多。

于 2013-06-06T09:36:05.460 回答
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
于 2013-06-07T06:26:23.440 回答
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 演示

上面的查询使用了两个 case 语句

  1. 对 Invoiceid 790109-1 的第一部分进行排序(例如:790709)
  2. 用 '-' 790109-1 拆分后对 Invoiceid 的第二部分进行排序(例如:1)

如需详细了解,请查看以下 SQLfiddle

SQLFiddle 详细演示

或使用“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
于 2013-06-07T10:23:00.447 回答
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
于 2013-06-09T06:18:09.323 回答
2

这里有很多好的答案,但我认为这可能是最紧凑的有效的 order by 子句:

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

演示:- SQL 小提琴

请注意,我在测试中添加了“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

于 2013-06-09T18:33:55.420 回答
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 )
于 2013-06-06T09:36:37.510 回答
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)

在上面,InvoiceId1InvoiceId2是 的组成部分InvoiceId外部 包括零件,select但仅用于演示目的 - 您无需在选择中执行此操作。

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

  • 当 中有破折号时InvoiceIdInvoiceId1将包含数字的第一部分并InvoiceId2包含第二部分。
  • 当没有破折号时,InvoiceId1将为空并InvoiceId2包含整个数字。

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

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

SQL Fiddle上的演示

于 2013-06-06T14:34:09.730 回答
1

将排序分为两部分:

SQL小提琴

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

结果

|      DATA |
-------------
|  790109-1 |
|  790109-2 |
| 790109-11 |
|    790711 |
于 2013-06-11T09:41:41.407 回答
0

尝试:

select invoiceid  ... order by Convert(decimal(18, 2), REPLACE(invoiceid, '-', '.'))
于 2013-05-01T10:44:20.110 回答