使用混合的字母数字数据对varchar字段进行排序

时间:2015-02-03 13:23:15

标签: sql-server tsql sql-server-2012

我在这里搜索并阅读了很多答案,但找不到能解答我问题的答案,(或者帮我自己找答案)。

我们有一个包含varchar显示字段的表,客户输入了谁的数据。 当我们显示结果时,我们的客户希望正确地订购结果"。

数据的样本如下:

"AAA 2 1 AAA"
"AAA 10 1 AAA"
"AAA 10 2 BAA"
"AAA 101 1 AAA"
"BAA 101 2 BBB"
"BAA 101 10 BBB"
"BAA 2 2 AAA"

按此列排序ASC返回:

1: "AAA 10 1 AAA"
2: "AAA 10 2 BAA"
3: "AAA 101 1 AAA"
4: "AAA 2 1 AAA"
5: "BAA 101 10 BBB"
6: "BAA 101 2 BBB"
7: "BAA 2 2 AAA"

客户希望第4行实际上是第一行(因为2在10之前),同样第7行在第4行和第5行之间,如下所示:

1: "AAA 2 1 AAA"
2: "AAA 10 1 AAA"
3: "AAA 10 2 BAA"
4: "AAA 101 1 AAA"
5: "BAA 2 2 AAA"
6: "BAA 101 10 BBB"
7: "BAA 101 2 BBB"

现在,真正的TRICKY位,对于此列中的数据,没有严格的规则;它完全取决于客户在这里放置的内容(上面显示的数据只是用来证明问题)。

任何帮助?

修改 了解这被称为"自然分选"大大改善了我的搜索结果 我将把这个问题的接受答案给予bash并相应更新: Natural (human alpha-numeric) sort in Microsoft SQL 2005

2 个答案:

答案 0 :(得分:1)

首先创建此功能

Create FUNCTION dbo.SplitAndJoin
(
  @delimited nvarchar(max),
  @delimiter nvarchar(100)
) RETURNS Nvarchar(Max) 
AS
BEGIN

declare @res nvarchar(max)

declare @t TABLE
(
-- Id column can be commented out, not required for sql splitting string
  id int identity(1,1), -- I use this column for numbering splitted parts
  val nvarchar(max)
)

  declare @xml xml
  set @xml = N'<root><r>' + replace(@delimited,@delimiter,'</r><r>') + '</r></root>'

  insert into @t(val)
  select
    r.value('.','varchar(max)') as item
  from @xml.nodes('//root/r') as records(r)

  SELECT @res = STUFF((SELECT ' ' + case when isnumeric(val) = 1 then RIGHT('00000000'+CAST(val AS VARCHAR(8)),8) else val end
              FROM @t
              FOR XML PATH('')), 1, 1, '') 

  RETURN @Res
END
GO
  

此函数获取一个以空格分隔的字符串并将其拆分为单词,然后再按空格将它们连接在一起,但如果该单词为数字,则会添加8个前导零

然后你使用这个查询

Select * from Test
order by dbo.SplitAndJoin(col1,' ')

Live result on SQL Fiddle

enter image description here

答案 1 :(得分:0)

  • 没有一致性,你只有蛮力
  • 没有规则,你的暴力是有限的

我已经用这段代码做了一些假设:如果它以3个字母字符开头,那么一个空格,一个数字(最多3个数字),让我们区别对待它。

这没什么特别的 - 只是字符串操作被强行赐给你&#34;某事&#34;。希望它能说明如果没有一致性和规则会有多痛苦!

DECLARE @t table (
   a varchar(50)
);

INSERT INTO @t (a)
  VALUES ('AAA 2 1 AAA')
       , ('AAA 10 1 AAA')
       , ('AAA 10 2 BAA')
       , ('AAA 101 1 AAA')
       , ('BAA 101 2 BBB')
       , ('BAA 101 10 BBB')
       , ('BAA 2 2 AAA')
       , ('Completely different')
;

; WITH step1 AS (
  SELECT a
       , CASE WHEN a LIKE '[A-Z][A-Z][A-Z] [0-9]%' THEN 1 ELSE 0 END As fits_pattern
       , CharIndex(' ', a) As first_space
  FROM   @t
)
, step2 AS (
  SELECT *
       , CharIndex(' ', a, first_space + 1) As second_space
       , CASE WHEN fits_pattern = 1 THEN Left(a, 3) ELSE 'ZZZ' END As first_part
       , CASE WHEN fits_pattern = 1 THEN SubString(a, first_space + 1, 1000) ELSE 'ZZZ' END As rest_of_it
  FROM   step1
)
, step3 AS (
  SELECT *
       , CASE WHEN fits_pattern = 1 THEN SubString(rest_of_it, 1, second_space - first_space - 1) ELSE 'ZZZ' END As second_part
  FROM   step2
)
SELECT *
     , Right('000' + second_part, 3) As second_part_formatted
FROM   step3
ORDER
    BY first_part
     , second_part_formatted
     , a
;

相关的,排序结果:

a                    
---------------------
AAA 2 1 AAA          
AAA 10 1 AAA         
AAA 10 2 BAA         
AAA 101 1 AAA        
BAA 2 2 AAA          
BAA 101 10 BBB       
BAA 101 2 BBB        
Completely different 

此代码可以大大改进/缩短。我只是把它弄得很冗长,以便让你清楚地了解所采取的措施。