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

Sql server 使用混合字母数字数据对varchar字段进行排序,sql-server,tsql,sql-server-2012,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"

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

我们有一个包含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"
现在,真正棘手的一点是,对于本专栏中的数据,没有硬性规定;这完全取决于客户在这里输入了什么(上面显示的数据只是为了证明问题的任意性)

有什么帮助吗

编辑: 得知这被称为“自然排序”,我的搜索结果得到了极大的改善 我将对这个问题给出一个公认的答案,并将相应地更新:

  • 如果没有一致性,你只有蛮力
  • 没有规则,你的暴力是有限的
我对这段代码做了一些假设:如果它以3个字母字符开始,然后是一个空格,然后是一个数字(最多3位),让我们以不同的方式对待它

这并没有什么特别之处——它只是粗暴地操纵字符串,给你一些“东西”。希望它能说明如果没有一致性和规则,这是多么痛苦

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 

此代码可以大大改进/缩短。为了让您更清楚地了解所采取的步骤,我将其保留为详细内容。

首先创建此函数

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


输入的内容似乎有一定的一致性。这表明正在输入特定的属性值,对吗?如果是这样的话,它们应该被分割成单独的字段供使用者写入,而不是单个文本字符串。但是为了解决当前的难题,我们需要一些规则来解释这些值,例如“总是以3个字符开始,然后是空格,然后是下一部分,然后是空格”。有了这些信息,你可以用锤子敲打数据,使其符合要求。恐怕不行-我们不能依赖任何一致性,这就是为什么这让我完全困惑的原因!可以肯定的是,我们只有一个varchar(100)数据字段。它可以包含数字,这些数字可以与字母字符分开。。。或者不是。当我们让客户输入他们想要的内容时,我们遇到的问题是什么/如果客户希望以某种方式对数据进行排序,那么客户需要提供如何对数据进行排序的规则。剩下的只是字符串解析。SQL恐怕没有魔法精灵灰尘。正如评论中提到的。。实际上没有模式。数据可以是1:“我希望这是第2位”,2:“我希望这是第10位”,3:“这应该是列表中的第1位”。。。然后,顺序应为3,1,2。人类的思维非常容易做出这样的顺序,而且似乎很自然地正确。。但是没有模式,否则我就不会被困在第一位了!:/@Sk93是模式,因此:“找到字符串中出现的第一个数字,并按此进行排序”?否。同样,没有模式。他们可以很容易地添加另一行“实际上,我希望这是第1行,而不是前面建议的第3行”。。然后这个会排在其他三个之前,因为字母和数字都在前面。@Sk93那么我恐怕你运气不好。计算机不是智能的。有些用户也不是;)如果他们接着加上“事实上,我希望这不是前面建议的第5行,而是第1行”,它实际上不是第一行,因为上面的前一个条目应该先出现,因为第5行高于第3行…@sk93你尝试过我的解决方案吗?
Select * from Test
order by dbo.SplitAndJoin(col1,' ')