【问题标题】:SQL alphanumerical sorting with non specific format非特定格式的 SQL 字母数字排序
【发布时间】:2021-11-01 20:45:13
【问题描述】:

我必须对格式不同的字符串进行排序,但我很难找到解决方案。为此在 stackoverflow 上尝试了几个选项,但它对我不起作用,因为它们适用于特定格式的数据。

这是我必须排序的数据示例。

12-ABC
1-ABC
ABC-10
ABC-11
ABC-100
2-ABCD
ABC-100A

我通过 ORDER BY 得到了这样的结果

1-ABC
12-ABC
2-ABCD
ABC-10
ABC-100
ABC-100A
ABC-11

但我想要这个

1-ABC
2-ABCD
12-ABC
ABC-10
ABC-11
ABC-100
ABC-100A

你会怎么做?

【问题讨论】:

  • 您想要的不是字母数字排序。排序对格式一无所知。无论如何,这背后的逻辑是什么?甚至不能说您是根据一个或另一个“字段”进行排序的。无论逻辑是什么,我怀疑如果您将这个多值字段拆分为具有正确类型的单独字段并编写一个简单的ORDER BY,这会更容易很多。即使100A 看起来也想成为两个不同的领域。您可能需要 2 个文本字段(描述、标志)和 1 个数字(ID)。 ORDER BY ID, Description, Flag 会工作
  • 我会先写下确切的排序规则,例如:1)数字在字母之前 2)如果值以数字开头,则首先按其数值排序 - 等等。这样你会发现,如何分析列值,如何将其拆分为可排序的部分,以什么顺序以及如何对部分进行排序等等。之后只需要编写代码,不需要反复试验:)
  • 如果你也有3A-ABCABC-11A怎么办?另外,如果一个值没有破折号(如30AACB),是一个空字符串,还是NULL?您能否在示例数据和所需结果中包含您希望如何处理所有这些其他边缘情况(或声明哪些是不可能的,例如 NOT NULL 约束,或检查长度约束或值是否包含破折号)?
  • 只是把一个巨大的扳手扔进去,像'17A12-AB1N9'这样的值呢?
  • 请提供足够的代码,以便其他人更好地理解或重现问题。

标签: sql sql-server string columnsorting


【解决方案1】:

这是一个复杂的问题。解析字符串不是 SQL 通常设计的,尤其是 SQL Server。

您正在尝试从字符串的两个部分中提取一个数字并按该数字排序。啊!这确实表明您在一个字符串中存储了多种类型的信息——这些信息在单独的列中可能会更好地表示。

也就是说,你可以为所欲为。这是一种适用于您在问题中提供的数据的方法:

select t.str
from (values ('1-ABC'),
             ('12-ABC'),
             ('2-ABCD'),
             ('ABC-10'),
             ('ABC-100'),
             ('ABC-100A'),
             ('ABC-11')
     ) t(str) cross apply
     (values (left(str, charindex('-', str + '-') - 1), stuff(str, 1, charindex('-', str), ''))
     ) v(part1, part2)
order by coalesce(try_convert(int, v.part1), 999999999),
         part1,
         try_convert(int, left(v.part2, patindex('%[^0-9]%', v.part2 + 'x') - 1)),
         part2;

Here 是一个 dbfiddle。

【讨论】:

    【解决方案2】:

    您可以将字符串拆分为数字和文本(在“-”上),然后按 num 列排序。比如:

    select iif((charindex('-', str) > 0), (cast(substring(str, 1, (charindex('-', str)-1)) as integer)), 9999) as num, str
    from (
        select '1-ABC' as str union all
        select '2-ABC' as str  union all
        select '12-ABC' as str  union all
        select 'ABC' as str
    ) tbl
    order by num, str
    

    这样,您将首先按数值排序,然后按字符串排序。在这里,我只是将一个非常高的数字添加到无数字值中,以便最后排序。

    【讨论】:

      【解决方案3】:

      感谢大家提供所有信息。我设法让这个东西在这个特殊情况下几乎完美地工作。

      select distinct PCE_NAM,
          --FIRST SECTION OF THE STRING
          --REPLICATE to fill 0s before numerics
          (CASE 
              WHEN CHARINDEX('-', PCE_NAM) = 0 then PCE_NAM
              WHEN CHARINDEX('-', PCE_NAM) > 0 and 
                  ISNUMERIC(LEFT(PCE_NAM, CHARINDEX('-', PCE_NAM) -1)) = 1 and
                  LEN(LEFT(PCE_NAM, CHARINDEX('-', PCE_NAM) -1)) <= 8
                  then REPLICATE('0', 8-LEN(LEFT(PCE_NAM, CHARINDEX('-', PCE_NAM) -1))) + LEFT(PCE_NAM, CHARINDEX('-', PCE_NAM) -1)
              ELSE LEFT(PCE_NAM, CHARINDEX('-', PCE_NAM) -1)
          END) as FirstPart,
          --SECOND SECTION OF THE STRING
          (CASE 
              WHEN CHARINDEX('-', PCE_NAM) = 0 then ''
              WHEN CHARINDEX('-', PCE_NAM) > 0 and 
                  ISNUMERIC(SUBSTRING(PCE_NAM, CHARINDEX('-', PCE_NAM)+1, LEN(PCE_NAM))) = 1 and
                  LEN(SUBSTRING(PCE_NAM, CHARINDEX('-', PCE_NAM)+1, LEN(PCE_NAM))) <= 8
                  then REPLICATE('0', 8-LEN(SUBSTRING(PCE_NAM, CHARINDEX('-', PCE_NAM)+1, LEN(PCE_NAM)))) + SUBSTRING(PCE_NAM, CHARINDEX('-', PCE_NAM)+1, LEN(PCE_NAM))
              ELSE SUBSTRING(PCE_NAM, CHARINDEX('-', PCE_NAM)+1, LEN(PCE_NAM))
          END) as SecondPart,
          --GET THE NUMERICS ONLY FROM THE SECOND SECTION SO CAN SORT PROPERLY
          (CASE 
              WHEN LEN(LEFT(SUBSTRING(PCE_NAM, CHARINDEX('-', PCE_NAM)+1, LEN(PCE_NAM)), PATINDEX('%[0-9][^0-9]%', SUBSTRING(PCE_NAM, CHARINDEX('-', PCE_NAM)+1, LEN(PCE_NAM))))) <= 8
                  then REPLICATE('0', 8-LEN(
                  LEFT(SUBSTRING(PCE_NAM, CHARINDEX('-', PCE_NAM)+1, LEN(PCE_NAM)), PATINDEX('%[0-9][^0-9]%', SUBSTRING(PCE_NAM, CHARINDEX('-', PCE_NAM)+1, LEN(PCE_NAM)))))) + 
                  LEFT(SUBSTRING(PCE_NAM, CHARINDEX('-', PCE_NAM)+1, LEN(PCE_NAM)), PATINDEX('%[0-9][^0-9]%', SUBSTRING(PCE_NAM, CHARINDEX('-', PCE_NAM)+1, LEN(PCE_NAM))))
              ELSE LEFT(SUBSTRING(PCE_NAM, CHARINDEX('-', PCE_NAM)+1, LEN(PCE_NAM)), PATINDEX('%[0-9][^0-9]%', SUBSTRING(PCE_NAM, CHARINDEX('-', PCE_NAM)+1, LEN(PCE_NAM))))
          END) as SecondPartF
      from PARTS
      order by FirstPart, SecondPartF, SecondPart
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2022-08-03
        • 2016-10-13
        • 2018-05-07
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多