【问题标题】:How do I sort a VARCHAR column in SQL server that contains words and numbers?如何在 SQL Server 中对包含单词和数字的 VARCHAR 列进行排序?
【发布时间】:2009-10-19 20:11:47
【问题描述】:

我有一个包含字母和数字的 varchar(100) 字段。 这些值通常采用 car 1、car 10、car 100、car 20 的形式。但这些值可以在数字前有任何单词。有没有办法对这些值进行数字排序,以便 2 号车排在 10 号车之前?谢谢。

【问题讨论】:

标签: sql sql-server tsql


【解决方案1】:

您必须弄清楚数据中的内容并将 car 2 拆分为 varchar car 和 int 2。

该模式可以像WORD SPACE NUMBER 一样简单,您可以使用PATINDEX 或CHARINDEX 与SUBSTRING 结合使用基于SPACE 拆分它。

然后就可以按两列排序了。

这是一个工作示例

SET NOCOUNT ON

Declare @Table table
(
    Id  INT Identity (1, 1),
    StringValue VarChar (30)
)

INSERT INTO @Table (StringValue) VALUES ('CAR 10')
INSERT INTO @Table (StringValue) VALUES ('CAR 20')
INSERT INTO @Table (StringValue) VALUES ('CAR 2')
INSERT INTO @Table (StringValue) VALUES ('CAR 3')
INSERT INTO @Table (StringValue) VALUES ('CAR 4')

INSERT INTO @Table (StringValue) VALUES ('SHIP 32')
INSERT INTO @Table (StringValue) VALUES ('SHIP 310')
INSERT INTO @Table (StringValue) VALUES ('SHIP 320')
INSERT INTO @Table (StringValue) VALUES ('SHIP 33')
INSERT INTO @Table (StringValue) VALUES ('SHIP 34')


SELECT Id, 
    SubString (StringValue, 1, CharIndex (' ', StringValue)) ObjectName,
    CONVERT (INT, SubString (StringValue, CharIndex (' ', StringValue), LEN (StringValue))) ObjectId
FROM @Table
ORDER BY 2, 3

SELECT Id, StringValue
FROM @Table
ORDER BY 
    SubString (StringValue, 1, CharIndex (' ', StringValue)),
    CONVERT (INT, SubString (StringValue, CharIndex (' ', StringValue), LEN (StringValue)))

【讨论】:

    【解决方案2】:

    输入不良和不一致的数据很难以编程方式修复。但是,您应该修复这些数据,而不是在您的 SELECT 中,这样您的 ORDER BY 才能工作,而是在数据中,这样您就不必再担心这个了。

    您应该考虑为相关数据的“单词”和“数字”部分创建单独的列。然后,您可以运行一个脚本,尝试将数据放入正确的列中,然后进行任何必要的手动跟进。您将更改应用程序逻辑,并可能更改前端以保持进入数据库的数据有效。

    任何不足都会导致数据排序无效。

    【讨论】:

    • 我支持你,如果经常需要这样排序,从性能角度来看唯一合理的解决方案是正确存储数据。这可以通过触发器或计算字段来完成(取决于如何拆分数据的复杂性)。
    【解决方案3】:

    编写一个重新格式化字符串的函数(例如,汽车 10 变为汽车 010),然后使用它来转换 SQL 查询中的列数据。您必须检查此解决方案的性能。

    或者...您可以按照上述相同的方式转换现有数据,以便在查询时无需应用任何函数即可在 SQL 中按字母顺序排序。

    【讨论】:

      【解决方案4】:
      WITH TABLE_NAME(NAME) AS
      (
      SELECT 'car 20'
      UNION ALL
      SELECT 'car 2'
      UNION ALL
      SELECT 'car 10'
      )
      SELECT
        *
      FROM TABLE_NAME
      ORDER BY
      SUBSTRING(NAME,1,CHARINDEX(' ',NAME,1)),CAST(SUBSTRING(NAME,CHARINDEX(' ',NAME,1)+1,100) AS INT)
      

      SUBSTRING(NAME,1,CHARINDEX(' ',NAME,1)) - 空格前的文本 CAST(SUBSTRING(NAME,CHARINDEX(' ',NAME,1)+1,100) AS INT) - 空格后的文本,转换为 int 以便正确排序

      【讨论】:

      • WITH 语法仅在 SQL Server 2005+ 上支持,并且 OP 没有提供他们正在使用的版本。
      • @rexem,WITH 只是为ORDER BY 生成示例数据,这里不是实际解决方案的一部分
      【解决方案5】:

      怎么样:

      SELECT
        col1, col1_ltrim
      , col1_sort 
          = CONVERT(INT
              , SUBSTRING(col1_ltrim,1
                , ISNULL(
                    NULLIF(PATINDEX('%[^0-9]%',col1_ltrim),0)-1
                  , LEN(col1_ltrim)
                  )
                )
              )
      FROM (  
        SELECT col1
        , col1_ltrim = STUFF(col1,1,PATINDEX('%[0-9]%',col1)-1,'')
        FROM (
          SELECT 'car 505' UNION ALL
          SELECT 'car 95' UNION ALL
          SELECT 'car 8776 blue' UNION ALL
          SELECT 'car'
          ) a (col1)
        ) a
      ORDER BY col1_sort 
      

      您可以将其包装在 UDF 中以保持理智:

      CREATE FUNCTION dbo.StringToInt (@string VARCHAR(128))
      RETURNS INT AS
      BEGIN
      
        SELECT @string = STUFF(@string,1,PATINDEX('%[0-9]%',@string)-1,'')
        RETURN CONVERT(INT
                , SUBSTRING(@string,1
                  , ISNULL(
                      NULLIF(PATINDEX('%[^0-9]%',@string),0)-1
                    , LEN(@string)
                    )
                  )
                )
      
      END
      
      GO
      
      SELECT col1, col1_sort = dbo.StringToInt(col1)
      FROM (
        SELECT 'car 505' UNION ALL
        SELECT 'car 95' UNION ALL
        SELECT 'car 8776 blue' UNION ALL
        SELECT 'car'
        ) a (col1)
      ORDER BY col1_sort
      

      【讨论】:

        【解决方案6】:

        您可以利用这一事实

        patindex('%...', _col_)
        

        匹配最后一个字符并且

        patindex('%...%', _col_)
        

        匹配任意数量的字符。因此,只要数字位于列值的末尾,您就可以轻松地从列中提取数值(StackOverflow 不会让我将 UNION 放在帖子中 - 将 UUU 替换为 UNION):

        select Rec,
        case
           when patindex('%[0-9]', Rec) > 0
           then left(Rec, patindex('%[0-9]%', Rec)-1)
           else Rec
        end,
        case
           when patindex('%[0-9]', Rec) > 0
           then cast(right(Rec, len(Rec)-patindex('%[0-9]%', Rec)+1) as int)
        end
        from (select 'SHIP34' Rec UUU select 'SHIP 33' UUU select 'SHIP 320' UUU select 'SHIP310' UUU select 'SHIP32' UUU select 'CAR 4X' UUU select 'CAR 4' UUU select 'CAR3' UUU select 'CAR 2' UUU select 'CAR20' UUU select 'CAR 10') TestData
        order by
           case
              when patindex('%[0-9]', Rec) > 0
              then left(Rec, patindex('%[0-9]%', Rec)-1)
              else Rec
           end,
           case
              when patindex('%[0-9]', Rec) > 0
              then cast(right(Rec, len(Rec)-patindex('%[0-9]%', Rec)+1) as int)
           end
        

        【讨论】:

          猜你喜欢
          • 2010-09-12
          • 2018-09-05
          • 2011-05-04
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2021-09-27
          • 2013-10-16
          • 1970-01-01
          相关资源
          最近更新 更多