简体   繁体   English

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

[英]Sort string as number in sql server

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
790109-11
790109-2

i have to sort it in increasing order by this number but since this is a varchar field it sorts in alphabetical order like this 我必须按此数字按递增顺序对其进行排序,但由于这是一个varchar字段,因此按字母顺序排序

790109-1
790109-11
790109-2
790711

in order to fix this i tried replacing the -(dash) with empty and then casting it as a number and then sorting on that 为了解决这个问题,我尝试用空替换 - (破折号),然后将其作为数字投射,然后对其进行排序

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

while this is better and sorts like this 虽然这更好,并且这样排序

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

Someone suggested to me to split invoice id on the - (dash ) and order by on the 2 split parts 有人建议我在 - (破折号)上拆分发票ID,在2个拆分部分按顺序拆分

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

which would work i think but how would i split the column. 哪个会工作我想但我怎么会拆分列。

The various split functions on the internet are those that return a table while in this case i would be requiring a scalar function. 互联网上的各种拆分功能是那些返回表格的功能,而在这种情况下我需要一个标量功能。

Are there any other approaches that can be used? 有没有其他方法可以使用? The data is shown in grid view and grid view doesn't support sorting on 2 columns by default ( i can implement it though :) ) so if any simpler approaches are there i would be very nice. 数据显示在网格视图中,并且网格视图默认情况下不支持在2列上进行排序(我可以实现它:))因此,如果有任何更简单的方法,我会非常好。

EDIT : thanks for all the answers. 编辑 :谢谢你的所有答案。 While every answer is correct i have chosen the answer which allowed me to incorporate these columns in the GridView Sorting with minimum re factoring of the sql queries. 虽然每个答案都是正确的,但我选择的答案允许我将这些列合并到GridView排序中,并最小化sql查询的因子。

Try this one - 试试这个 -

Query: 查询:

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)

Output: 输出:

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

Judicious use of REVERSE , CHARINDEX , and SUBSTRING , can get us what we want. 明智地使用REVERSECHARINDEXSUBSTRING ,可以得到我们想要的东西。 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: 请注意,您实际需要的只是ORDER BY子句,其余的只是为了显示我的工作,它是这样的:

  • 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 . 删除一个额外的字符以获取数字部分,并将其转换为int Fortunately we get a break from SQL Server in that this conversion gives zero for an empty string. 幸运的是,我们从SQL Server中获得了一个突破,因为这个转换为空字符串提供了零。
  • Finally, having got these two pieces, we simple ORDER BY (the number without any affix) and then by (the numeric value of the affix). 最后,有了这两个部分,我们简单的ORDER BY (没有任何词缀的数字),然后是(词缀的数值)。 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. 如果SQL Server允许我们说SUBSTRING(value, start)来获取从该点开始的字符串,但是它没有,那么代码会更简洁,所以我们不得不说SUBSTRING(value, start, LEN(value))很多。

Try this 试试这个

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 SQLFiddle演示

Above query uses two case statements 上面的查询使用两个case语句

  1. Sorts first part of Invoiceid 790109-1 (eg: 790709) 排序Invoiceid 790109-1的第一部分(例如:790709)
  2. Sorts second part of Invoiceid after splitting with '-' 790109-1 (eg: 1) 在使用' - '790109-1分割后对Invoiceid的第二部分进行排序(例如:1)

For detailed understanding check the below SQLfiddle 有关详细信息,请查看下面的SQLfiddle

SQLFiddle Detailed Demo SQLFiddle详细演示

OR use 'CHARINDEX' 或使用'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

Order by each part separately is the simplest and reliable way to go, why look for other approaches? 每个部分分别订购是最简单可靠的方法,为什么要寻找其他方法? Take a look at this simple query. 看看这个简单的查询。

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

Plenty of good answers here, but I think this one might be the most compact order by clause that is effective: 这里有很多好的答案,但我认为这个可能是有效的最紧凑的order by子句:

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

Demo: - SQL Fiddle 演示: - SQL小提琴

Note, I added the '790709' version to my test, since some of the methods listed here aren't treating the no-suffix version as lesser than the with-suffix versions. 注意,我在我的测试中添加了'790709'版本,因为这里列出的一些方法并没有将后缀版本视为小于后缀版本。

If your invoiceID varies in length, before the '-' that is, then you'd need: 如果您的invoiceID长度不同,在' - '之前,那么您需要:

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

Demo with varying lengths before the dash: SQL Fiddle 在破折号之前使用不同长度的演示: SQL小提琴

My version: 我的版本:

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

You can implement this as a new column to your table: 您可以将其实现为表的新列:

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 )

One way is to split InvoiceId into its parts, and then sort on the parts. 一种方法是将InvoiceId拆分为其部分,然后对部件进行排序。 Here I use a derived table, but it could be done with a CTE or a temporary table as well. 这里我使用派生表,但也可以使用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)

In the above, InvoiceId1 and InvoiceId2 are the component parts of InvoiceId . 在上文中, InvoiceId1InvoiceId2是组成部件InvoiceId The outer select includes the parts, but only for demonstration purposes - you do not need to do this in your select. 外部 select包括部件,但仅用于演示目的 - 您不需要在选择中执行此操作。

The derived table (the inner select ) grabs the InvoiceId as well as the component parts. 派生表(内部select )抓取InvoiceId以及组件部分。 The way it works is this: 它的工作方式是这样的:

  • When there is a dash in InvoiceId , InvoiceId1 will contain the first part of the number and InvoiceId2 will contain the second. InvoiceId有短划线时, InvoiceId1将包含数字的第一部分, InvoiceId2将包含第二部分。
  • When there is not a dash, InvoiceId1 will be empty and InvoiceId2 will contain the entire number. 如果没有破折号, InvoiceId1将为空, InvoiceId2将包含整个数字。

The second case above (no dash) is not optimal because ideally InvoiceId1 would contain the number and InvoiceId2 would be empty. 上面的第二种情况(没有破折号)不是最佳的,因为理想情况下InvoiceId1将包含数字, InvoiceId2将为空。 To make the inner select work optimally would decrease the readability of the select. 为了使内部选择最佳地工作会降低选择的可读性。 I chose the non-optimal, more readable, approach since it is good enough to allow for sorting. 我选择了非最佳,更易读的方法,因为它足以允许排序。

This is why the ORDER BY clause tests for the length - it needs to handle the two cases above. 这就是ORDER BY子句测试长度的原因 - 它需要处理上面的两种情况。

Demo at SQL Fiddle SQL Fiddle演示

Break the sort into two sections: 将排序分为两部分:

SQL Fiddle SQL小提琴

MS SQL Server 2008 Schema Setup : 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'

Query 1 : 查询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 |

尝试:

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

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM