如何对SQL server中包含数字的VARCHAR列进行排序?

如何对SQL server中包含数字的VARCHAR列进行排序?,sql,tsql,Sql,Tsql,我在SQLServer2000数据库中有一个VARCHAR列,它可以包含字母或数字。这取决于客户在前端如何配置应用程序 当它确实包含数字时,我希望它按数字排序,例如“1”、“2”、“10”而不是“1”、“10”、“2”。仅包含字母或字母和数字(如“A1”)的字段可以按正常字母顺序排序。例如,这将是一个可接受的排序顺序 1 2 10 A B B1 实现这一目标的最佳方式是什么 有几种可能的方法可以做到这一点 一个是 SELECT ... ORDER BY CASE WHEN IS

我在
SQLServer2000
数据库中有一个
VARCHAR
列,它可以包含字母或数字。这取决于客户在前端如何配置应用程序

当它确实包含数字时,我希望它按数字排序,例如“1”、“2”、“10”而不是“1”、“10”、“2”。仅包含字母或字母和数字(如“A1”)的字段可以按正常字母顺序排序。例如,这将是一个可接受的排序顺序

1
2
10
A
B
B1

实现这一目标的最佳方式是什么

有几种可能的方法可以做到这一点

一个是

SELECT
 ...
ORDER BY
  CASE 
    WHEN ISNUMERIC(value) = 1 THEN CONVERT(INT, value) 
    ELSE 9999999 -- or something huge
  END,
  value
ORDER BY的第一部分将所有内容转换为int(非数字的值很大,最后排序),然后最后一部分处理字母

请注意,对于大量数据,此查询的性能可能至少相当糟糕

SELECT FIELD FROM TABLE
ORDER BY 
  isnumeric(FIELD) desc, 
  CASE ISNUMERIC(test) 
    WHEN 1 THEN CAST(CAST(test AS MONEY) AS INT)
    ELSE NULL 
  END,
  FIELD
根据,您需要先转换为MONEY,然后转换为INT,以避免将“$”作为数字排序

select
  Field1, Field2...
from
  Table1
order by
  isnumeric(Field1) desc,
  case when isnumeric(Field1) = 1 then cast(Field1 as int) else null end,
  Field1
这将按照您在问题中给出的顺序返回值

在所有这些转换进行的情况下,性能不会太好,因此另一种方法是向表中添加另一列,在该表中存储数据的整数副本,然后依次排序。这显然需要对插入或更新表中数据的逻辑进行一些更改,以填充这两列。或者,在插入或更新数据时,在表上放置触发器以填充第二列。

这似乎有效:

select your_column  
from your_table  
order by   
case when isnumeric(your_column) = 1 then your_column else 999999999 end,  
your_column   

一种可能的解决方案是在数字值的前面填充一个字符,以便所有值都具有相同的字符串长度

以下是使用该方法的示例:

选择MyColumn
从MyTable
订购人
大小写为数字(MyColumn)
当1复制('0',100-Len(MyColumn))+MyColumn时
else菌柱
结束
100
应替换为该列的实际长度

SELECT *, CONVERT(int, your_column) AS your_column_int
FROM your_table
ORDER BY your_column_int


我认为这两种方法都相当方便。

我用一种非常简单的方法解决了这个问题,将其写在“订单”部分

这似乎很有效,事实上我有以下分类:

16079   Customer X 
016082  Customer Y
16413   Customer Z

因此,
16082
前面的
0
被认为是正确的。

您始终可以将varchar列转换为bigint,因为integer可能太短

select cast([yourvarchar] as BIGINT)
但是你应该始终注意字母字符

where ISNUMERIC([yourvarchar] +'e0') = 1
+'e0'来自

这将导致你的声明

SELECT
  *
FROM
  Table
ORDER BY
   ISNUMERIC([yourvarchar] +'e0') DESC
 , LEN([yourvarchar]) ASC
第一个排序列将把数字放在顶部。 第二个按长度排序,因此10将在0001之前(这是愚蠢的?!)

这导致了第二个版本:

SELECT
      *
    FROM
      Table
    ORDER BY
       ISNUMERIC([yourvarchar] +'e0') DESC
     , RIGHT('00000000000000000000'+[yourvarchar], 20) ASC
第二列现在用“0”正确填充,因此自然排序将带前导零(0,01,100100…)的整数按正确顺序排列(正确!)——但所有字母都将用“0”-字符增强(性能)

所以第三个版本:

 SELECT
          *
        FROM
          Table
        ORDER BY
           ISNUMERIC([yourvarchar] +'e0') DESC
         , CASE WHEN ISNUMERIC([yourvarchar] +'e0') = 1
                THEN RIGHT('00000000000000000000' + [yourvarchar], 20) ASC
                ELSE LTRIM(RTRIM([yourvarchar]))
           END ASC

现在,数字首先被填充为“0”-字符(当然,长度20可以增加)-这将正确排序数字-而字母只被修剪

此查询对您很有帮助。在此查询中,数据类型为varchar的列按良好顺序排列。例如-在此列中,数据为:-G1、G34、G10、G3。因此,运行此查询后,您将看到结果:-G1、G10、G3、G34

 SELECT *,
       ROW_NUMBER()OVER(ORDER BY CASE WHEN ISNUMERIC (ID)=1 THEN CONVERT(NUMERIC(20,2),SUBSTRING(Id, PATINDEX('%[0-9]%', Id), LEN(Id)))END DESC)Rn ---- numerical
        FROM
            (

        SELECT '1'Id UNION ALL
        SELECT '25.20' Id UNION ALL

    SELECT 'A115' Id UNION ALL
    SELECT '2541' Id UNION ALL
    SELECT '571.50' Id UNION ALL
    SELECT '67' Id UNION ALL
    SELECT 'B48' Id UNION ALL
    SELECT '500' Id UNION ALL
    SELECT '147.54' Id UNION ALL
    SELECT 'A-100' Id
    )A

    ORDER BY 
    CASE WHEN ISNUMERIC (ID)=0                                /* alphabetical sort */ 
         THEN CASE WHEN PATINDEX('%[0-9]%', Id)=0
                   THEN LEFT(Id,PATINDEX('%[0-9]%',Id))
                   ELSE LEFT(Id,PATINDEX('%[0-9]%',Id)-1)
              END
    END DESC
SELECT *,
       (CASE WHEN ISNUMERIC(column_name) = 1 THEN 0 ELSE 1 END) IsNum
FROM table_name 
ORDER BY IsNum, LEN(column_name), column_name;

这可能会对你有所帮助,当我遇到同样的问题时,我已经尝试过了

选择*
从选项卡

按IIF排序(TRY_CAST(val AS INT)为NULL,1,0),TRY_CAST(val AS INT)

这些表往往是简单的“参考”表,并且没有太多的行(最多几百行,因此性能不是主要问题)根据我在这里链接的专家交换文章:我认为您必须先转换为MONEY,然后转换为INT,以避免将“$”读作数字。我发现我们测试数据库中的一个字段包含123456789001234567890,这会导致其他解决方案出现问题。诚然,这不太可能发生在实时客户数据库上,但如果发生了,此解决方案将能够成功处理它。必须将char(0)更改为“0”才能使其正常工作。。。否则,这是可靠的。这里给出的其他解决方案不适用于降序排序,如果表上有null,char(0)是null,这会导致结果为null,因此应该使用“0”来代替。非常感谢您,只要您在上面的示例中将char(0)替换为0,这就很好了!很好!您可以在结尾后添加“desc”以按其他方向排序。在这种情况下,字符串将首先显示。我喜欢这个解决方案实际上不在结果输出中填充数字的方式!嗯,是不是。。。我现在已经修好了。我犯了一个典型的错误,在测试后摆弄一个有效的解决方案!很好,谢谢。+1简单明了。不像其他的,这是超级容易一瞥和理解。我唯一的问题是,如果数据不是数字,它还会排序吗?正如OP所说,
你的_列
也包括字母。您建议的解决方案不适用于包括字母在内的值。还请注意,IsNumeric为值24e4和12d34返回1。如果遇到此问题,请访问此处。看到你的问题,注意到一个错误的假设。您说过“只包含字母或字母和数字(如‘A1’)的字段可以按正常的字母顺序排序。”尝试对A1、A2和A10进行排序。你会得到A1,A10,A2。现在我已经完成了问题部分,我将在下面寻找答案。希望我能找到一个:)@anarkhalilov,是吗?@ahsteele,老实说,三年半后我很难回忆起它。@anarkhalilov我以为你不会,但我觉得这是值得的。谢谢回复。我在varchar字段中只有需要排序的数值。这对我有用!
 SELECT *,
       ROW_NUMBER()OVER(ORDER BY CASE WHEN ISNUMERIC (ID)=1 THEN CONVERT(NUMERIC(20,2),SUBSTRING(Id, PATINDEX('%[0-9]%', Id), LEN(Id)))END DESC)Rn ---- numerical
        FROM
            (

        SELECT '1'Id UNION ALL
        SELECT '25.20' Id UNION ALL

    SELECT 'A115' Id UNION ALL
    SELECT '2541' Id UNION ALL
    SELECT '571.50' Id UNION ALL
    SELECT '67' Id UNION ALL
    SELECT 'B48' Id UNION ALL
    SELECT '500' Id UNION ALL
    SELECT '147.54' Id UNION ALL
    SELECT 'A-100' Id
    )A

    ORDER BY 
    CASE WHEN ISNUMERIC (ID)=0                                /* alphabetical sort */ 
         THEN CASE WHEN PATINDEX('%[0-9]%', Id)=0
                   THEN LEFT(Id,PATINDEX('%[0-9]%',Id))
                   ELSE LEFT(Id,PATINDEX('%[0-9]%',Id)-1)
              END
    END DESC
SELECT *,
       (CASE WHEN ISNUMERIC(column_name) = 1 THEN 0 ELSE 1 END) IsNum
FROM table_name 
ORDER BY IsNum, LEN(column_name), column_name;