【问题标题】:Alphanumeric sort on nvarchar(50) columnnvarchar(50) 列上的字母数字排序
【发布时间】:2018-07-22 18:55:51
【问题描述】:

我正在尝试编写一个查询,该查询将返回按字母数字列 Code 排序的数据。

以下是我的查询:

  SELECT * 
  FROM <<TableName>>
    CROSS APPLY (SELECT PATINDEX('[A-Z, a-z][0-9]%', [Code]),
          CHARINDEX('', [Code]) ) ca(PatPos, SpacePos)
    CROSS APPLY (SELECT CONVERT(INTEGER, CASE WHEN ca.PatPos = 1 THEN 
          SUBSTRING([Code], 2,ISNULL(NULLIF(ca.SpacePos,0)-2, 8000)) ELSE NULL END),
   CASE WHEN ca.PatPos = 1 THEN LEFT([Code], 
          ISNULL(NULLIF(ca.SpacePos,0)-0,1)) ELSE [Code] END) ca2(OrderBy2, OrderBy1)
   WHERE [TypeID] = '1'

输出:

FFS1
FFS2
...
FFS12
FFS1.1
FFS1.2
...
FFS1.1E
FFS1.1R
...
FFS12.1
FFS12.2
FFS.12.1E
FFS12.1R
FFS12.2E
FFS12.2R

期望的输出:

FFS1
FFS1.1
FFS1.1E
FFS1.1R
....
FFS12
FFS12.1
FFS12.1E
FFS12.1R

我遗漏了什么或忽略了什么?

编辑:

让我尝试更好地详细说明表格内容。有 FFS1 - FFS12 的记录。这些分为 X 个子项,即 FFS1.1 - FFS1.X 到 FFS12.1 - FFS12.X。 E 和 R 不是拼写错误,每个子记录都有两个关联代码:FFS1.1E 和 FFS1.1R。

另外,我尝试使用 ORDER BY,但它排序为

FFS1 ... FFS10 FFS2

【问题讨论】:

  • 如果要对查询输出进行排序,则必须使用order by...
  • 您的预期输出对所有场景都没有帮助? 'FFS2' 应该在哪里? 'FFS.12.1E'(与您的其他示例值采用不同的格式)呢?
  • 您能否将表格的 ddl 附加到插入脚本和预期输出中。
  • 您在这里苦苦挣扎的原因是您的表格设计未标准化。您将两条信息放在一个元组中。这违反了 1NF。修正你的设计,查询很简单。
  • @SeanLange 基于此评论,我正在使用表中的排序列来处理此问题。查询现在很简单,谢谢!

标签: sql sql-server sorting select-query


【解决方案1】:

这适用于由点分隔的任何数量的部分。每个部分分别按字母数字排序。

DECLARE @YourValues TABLE(ID INT IDENTITY, SomeVal VARCHAR(100));
INSERT INTO @YourValues VALUES
 ('FFS1')
,('FFS2')
,('FFS12')
,('FFS1.1')
,('FFS1.2')
,('FFS1.1E')
,('FFS1.1R')
,('FFS12.1')
,('FFS12.2')
,('FFS.12.1E')
,('FFS12.1R')
,('FFS12.2E')
,('FFS12.2R');

--查询

WITH Splittable AS
(
    SELECT ID
          ,SomeVal
          ,CAST(N'<x>' + REPLACE(SomeVal,'.','</x><x>') + N'</x>' AS XML) AS Casted
    FROM @YourValues 
)
,Parted AS
(
    SELECT Splittable.*
          ,ROW_NUMBER() OVER(ORDER BY (SELECT NULL)) AS PartNmbr
          ,A.part.value(N'text()[1]','nvarchar(max)') AS Part
    FROM Splittable
    CROSS APPLY Splittable.Casted.nodes(N'/x') AS A(part)
)
,AddSortCrit AS
(
    SELECT ID 
          ,SomeVal
          ,(SELECT LEFT(x.Part + REPLICATE(' ',10),10) AS [*]
            FROM Parted AS x
            WHERE x.ID=Parted.ID
            ORDER BY PartNmbr
            FOR XML PATH('')
           ) AS SortColumn
    FROM Parted
    GROUP BY ID,SomeVal
)
SELECT ID
      ,SomeVal
FROM AddSortCrit 
ORDER BY SortColumn;

结果

ID  SomeVal
10  FFS.12.1E
1   FFS1
4   FFS1.1
6   FFS1.1E
7   FFS1.1R
5   FFS1.2
3   FFS12
8   FFS12.1
11  FFS12.1R
9   FFS12.2
12  FFS12.2E
13  FFS12.2R
2   FFS2

一些解释:

第一个 CTE 会将您的代码转换为 XML,这样可以分别处理每个部分。
第二个 CTE 将每个部分连同一个数字一起返回。
第三个 CTE 重新连接您的代码,但每个部分都填充到 10 个字符的长度。
最终的SELECTORDER BY 中使用了这个新的single-string-per-row

最后提示:

这个设计很糟糕!您不应该将这些值存储在连接的字符串中......将它们存储在单独的列中并将它们放在一起仅用于输出/表示层。这样做可以避免这个相当丑陋的小提琴......

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-05-05
    • 2017-06-05
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多