1

我有一个名为 MR 的列,它是一个 varchar。当我使用 ORDER BY 运行查询时,它似乎没有正确排序。

select MR, LName, FName 
from users
order by MR

结果:

MR        | LNAME | FNAME
----------+-------+-------
1234-234  | HEN   | LO
2343MA2   | SY    | JACK
MR20001   | LINA  | MARY
MR200011  | TEST  | CASE
MR20002   | KO    | MIKE

为什么 MR200011 显示在 MR20002 之前?关于如何正确排序的任何想法?MR 的格式不固定。

4

3 回答 3

2

您是按字符串排序,而不是按数字的值。位置 7 中的字符是正在比较的差异:

MR200011 
MR20002 
      ^

因为'2' > '1',这是你最终得到的顺序。从不比较第 8 个字符,因为基于字符的排序顺序不依赖于它。

要“修复”此问题,请创建一个存储函数,该函数采用您的 varchar 值,并返回一个新的“排序字符串”,它将数字组件填充到固定长度。

例如

MR20002  -> MR0020002
MR200011 -> MR0200011

但更重要的是,如果你有两个数字块,它们不会被破坏:

A1234-234  -> A000000001234-000000000234
A1234-5123 -> A000000001234-000000005123

以下函数在 sql-server 上执行此转换 - 您必须为 mysql 调整此函数:

create function dbo.get_numeric_sort_key(@value varchar(100)) 
    returns varchar(200)
as
begin
   declare @pad_characters varchar(12)
   declare @numeric_block varchar(12)
   declare @output varchar(200)
   set @pad_characters = '000000000000'
   set @output = ''
   set @numeric_block = ''

   declare @idx int
   declare @len int
   declare @char char(1)
   set @idx = 1
   set @len = len(@value)
   while @idx <= @len
   begin
     set @char = SUBSTRING(@value, @idx, 1)
     if @char in ('0','1','2','3','4','5','6','7','8','9') 
     begin
        set @numeric_block = @numeric_block + @char
     end
     else
     begin
        if (@numeric_block <> '')
        begin
          set @output = @output + right(@pad_characters + @numeric_block, 12)
          set @numeric_block = ''
        end
        set @output = @output + @char
     end
     set @idx = @idx + 1
   end

   if (@numeric_block <> '')
     set @output = @output + right(@pad_characters + @numeric_block, 12)

   return @output
end

然后更改您的order by子句以使用新功能:

select MR, LName, FName 
from users 
order by dbo.get_numeric_sort_key(MR)

如果您有大量数据,则值得在表定义的末尾添加一个计算字段(由此函数填充),这样您就不必每次运行此查询时都进行扫描。

于 2013-01-24T09:21:43.620 回答
0

只有当所有条目的长度都固定时,数字和字母的组合才能正确排序。在您的情况下,MR200011 和 MR20002 的长度不相等,并且根据 MR200011 MR20002 进行排序?第 8 个字符不见了

于 2013-01-24T09:22:45.180 回答
0

也许这个查询看起来不太好,但它会按照你想要的顺序对行进行排序:

select
  MR,
  LName,
  FName
from (
  select
    MR,
    LName,
    FName,
    least(
      case when locate('0', MR)>0 then locate('0', MR) else length(MR)+1 end,
      case when locate('1', MR)>0 then locate('1', MR) else length(MR)+1 end,
      case when locate('2', MR)>0 then locate('2', MR) else length(MR)+1 end,
      case when locate('3', MR)>0 then locate('3', MR) else length(MR)+1 end,
      case when locate('4', MR)>0 then locate('4', MR) else length(MR)+1 end,
      case when locate('5', MR)>0 then locate('5', MR) else length(MR)+1 end,
      case when locate('6', MR)>0 then locate('6', MR) else length(MR)+1 end,
      case when locate('7', MR)>0 then locate('7', MR) else length(MR)+1 end,
      case when locate('8', MR)>0 then locate('8', MR) else length(MR)+1 end,
      case when locate('9', MR)>0 then locate('9', MR) else length(MR)+1 end) pos
  from users
  ) users_pos
order by
  left(MR, pos-1),
  mid(MR, pos, length(MR)-pos+1)+0

在子查询 users_pos 我正在计算一个数字的第一个位置,然后我按left(MR, pos-1)哪个是字符串的非数字开头排序,哪个是字符串mid(MR, pos, length(MR)-pos+1)+0的数字部分,添加 0 将转换为数字并以数字形式排序(因此 20002 在 200011 之前)。

看到它在这里工作。

于 2013-01-24T09:48:16.513 回答