Microsoft SQL 2005 中的自然(人类字母数字)排序

Posted

技术标签:

【中文标题】Microsoft SQL 2005 中的自然(人类字母数字)排序【英文标题】:Natural (human alpha-numeric) sort in Microsoft SQL 2005 【发布时间】:2010-09-07 06:20:04 【问题描述】:

我们有一个大型数据库,在该数据库上进行 DB 端分页。这很快,可以在几分之一秒内从数百万条记录中返回一个包含 50 行的页面。

用户可以定义自己的排序,基本上是选择要排序的列。列是动态的 - 一些具有数值、一些日期和一些文本。

虽然大多数按预期排序,但文本的排序方式很愚蠢。好吧,我说笨,这对计算机来说是有意义的,但会让用户感到沮丧。

例如,按字符串记录 id 排序会给出如下结果:

rec1
rec10
rec14
rec2
rec20
rec3
rec4

...等等。

我希望这考虑到数字,所以:

rec1
rec2
rec3
rec4
rec10
rec14
rec20

我无法控制输入(否则我只会在前导 000 中格式化)并且我不能依赖单一格式 - 有些是像“alpha code-dept code-rec id”之类的东西”。

我知道在 C# 中执行此操作的几种方法,但无法拉出所有记录来对它们进行排序,因为那样会很慢。

有人知道在 Sql server 中快速应用自然排序的方法吗?


我们正在使用:

ROW_NUMBER() over (order by field name asc)

然后我们按那个来分页。

我们可以添加触发器,但我们不会。他们所有的输入都是参数化的等等,但我不能改变格式——如果他们输入“rec2”和“rec10”,他们希望它们会像这样以自然顺序返回。


我们为不同的客户提供了遵循不同格式的有效用户输入。

可能会选择 rec1、rec2、rec3、... rec100、rec101

而另一个可能会去:grp1rec1,grp1rec2,... grp20rec300,grp20rec301

当我说我们无法控制输入时,我的意思是我们不能强迫用户更改这些标准 - 它们具有像 grp1rec1 这样的值,我不能将其重新格式化为 grp01rec001,因为这会改变使用的东西用于查找和链接到外部系统。

这些格式变化很大,但通常是字母和数字的混合体。

在 C# 中对它们进行排序很容易 - 只需将其分解为 "grp", 20, "rec", 301 ,然后依次比较序列值。

但是可能有数百万条记录并且数据是分页的,我需要在 SQL 服务器上进行排序。

SQL 服务器按值排序,而不是比较 - 在​​ C# 中,我可以将值拆分出来进行比较,但在 SQL 中,我需要一些逻辑来(非常快速地)获得一致排序的单个值。

@moebius - 你的答案可能有效,但为所有这些文本值添加排序键确实感觉是一种丑陋的妥协。

【问题讨论】:

有一个关于自然排序的Coding Horror article。从 cmets 看来,此功能在 SQL Server 中不可用。 这个问题有点老了,但我添加了一个我想出的基于 CLR 的解决方案,这可能对其他人有帮助...... 虽然@RedFilter 的答案以及Roman Starkov 对RedFilter 答案的改进都很好,但最佳解决方案是SQL Server 通过Collat​​ion 属性在内部处理这个问题。这在操作系统中已经成为可能,因为它在文件资源管理器中按名称排序文件时使用(可能从 Windows 7 开始)。请投票支持我的 Microsoft Connection 建议,以便将此功能内置到 SQL Server 中,以便它真正实现:connect.microsoft.com/SQLServer/feedback/details/2932336/… 微软已经表示here on the Azure feedback portal 如果获得足够的选票,他们将支持 SQL Server 进行自然排序。 FWIW,我对它进行了尝试,我对结果感到相当满意。在本页最黑暗的深处,以我的理智为食,在下面的某个地方回答。 【参考方案1】:
order by LEN(value), value

并不完美,但在很多情况下都能很好地工作。

【讨论】:

如果数据是rec10aarec14b,则会中断。 第二个@OrbMan,更糟糕的是它打破了zzzaaaa【参考方案2】:

我见过的大多数基于 SQL 的解决方案在数据变得足够复杂(例如,其中的数字超过一两个)时都会崩溃。最初我尝试在 T-SQL 中实现满足我要求的 NaturalSort 函数(除其他外,处理字符串中的任意数量的数字),但性能方式太慢了。

最终,我在 C# 中编写了一个标量 CLR 函数以实现自然排序,即使使用未经优化的代码,从 SQL Server 调用它的性能也快得令人眼花缭乱。它具有以下特点:

将正确排序前 1000 个字符左右(很容易在代码中修改或制成参数) 正确排序小数,因此 123.333 在 123.45 之前 由于上述原因,可能无法正确排序 IP 地址等内容;如果您希望有不同的行为,请修改代码 支持对包含任意数量数字的字符串进行排序 将正确排序长达 25 位的数字(很容易在代码中修改或制成参数)

代码在这里:

using System;
using System.Data.SqlTypes;
using System.Text;
using Microsoft.SqlServer.Server;

public class UDF

    [SqlFunction(DataAccess = DataAccessKind.None, IsDeterministic=true)]
    public static SqlString Naturalize(string val)
    
        if (String.IsNullOrEmpty(val))
            return val;

        while(val.Contains("  "))
            val = val.Replace("  ", " ");

        const int maxLength = 1000;
        const int padLength = 25;

        bool inNumber = false;
        bool isDecimal = false;
        int numStart = 0;
        int numLength = 0;
        int length = val.Length < maxLength ? val.Length : maxLength;

        //TODO: optimize this so that we exit for loop once sb.ToString() >= maxLength
        var sb = new StringBuilder();
        for (var i = 0; i < length; i++)
        
            int charCode = (int)val[i];
            if (charCode >= 48 && charCode <= 57)
            
                if (!inNumber)
                
                    numStart = i;
                    numLength = 1;
                    inNumber = true;
                    continue;
                
                numLength++;
                continue;
            
            if (inNumber)
            
                sb.Append(PadNumber(val.Substring(numStart, numLength), isDecimal, padLength));
                inNumber = false;
            
            isDecimal = (charCode == 46);
            sb.Append(val[i]);
        
        if (inNumber)
            sb.Append(PadNumber(val.Substring(numStart, numLength), isDecimal, padLength));

        var ret = sb.ToString();
        if (ret.Length > maxLength)
            return ret.Substring(0, maxLength);

        return ret;
    

    static string PadNumber(string num, bool isDecimal, int padLength)
    
        return isDecimal ? num.PadRight(padLength, '0') : num.PadLeft(padLength, '0');
    

要注册它以便您可以从 SQL Server 调用它,请在查询分析器中运行以下命令:

CREATE ASSEMBLY SqlServerClr FROM 'SqlServerClr.dll' --put the full path to DLL here
go
CREATE FUNCTION Naturalize(@val as nvarchar(max)) RETURNS nvarchar(1000) 
EXTERNAL NAME SqlServerClr.UDF.Naturalize
go

然后,你可以这样使用它:

select *
from MyTable
order by dbo.Naturalize(MyTextField)

注意:如果您在 SQL Server 中遇到错误,在 .NET Framework 中执行用户代码被禁用。启用“clr enabled”配置选项。,按照说明here 启用它。确保在这样做之前考虑到安全隐患。如果您不是数据库管理员,请确保在对服务器配置进行任何更改之前与您的管理员讨论此问题。

注意2:此代码不正确支持国际化(例如,假设小数点标记为“.”,未针对速度进行优化等。欢迎提出改进建议!

编辑:将函数重命名为 Naturalize 而不是 NaturalSort,因为它不进行任何实际排序。

【讨论】:

抱歉添加到旧线程,如果您使用[SqlFunction(DataAccess = DataAccessKind.None, IsDeterministic=true)] 代替,它将提高性能。由于 SQL 服务器的优化方式。 虽然“未针对速度进行优化”,但在性能方面与我在下面的答案相比如何? @Seph 我没有测试过,但我猜 CLR 方法会快得多。每次我将 CLR 字符串操作与原生 SQL 操作进行比较时,我发现它的速度要快一个数量级。 很好的答案,谢谢!在此创建索引有点棘手,如果需要索引,请参阅 my answer 中的注释。 +20 表示@NoLifeKing 的建议(显然是这个答案的 +1)。 DataAccessKind 的默认值为None,因此只需将其删除就足够了,但将其作为Read 包含在此处会对性能产生明显的负面影响。设置IsDeterministic=true 绝对有帮助。另请参阅我留下的关于支持我的建议(此处:***.com/questions/34509/…)将其作为排序选项内置到 SQL Server 中的问题(上面)的评论 :-)。谢谢!【参考方案3】:

我知道这是一个老问题,但我只是遇到了它,因为它没有得到公认的答案。

我一直使用类似这样的方式:

SELECT [Column] FROM [Table]
ORDER BY RIGHT(REPLICATE('0', 1000) + LTRIM(RTRIM(CAST([Column] AS VARCHAR(MAX)))), 1000)

唯一常见的问题是如果您的列不会转换为 VARCHAR(MAX),或者如果 LEN([Column]) > 1000(但如果需要,您可以将 1000 更改为其他值) ,但您可以根据需要使用这个粗略的想法。

这也比普通的 ORDER BY [Column] 性能差得多,但它确实为您提供了 OP 中要求的结果。

编辑:为了进一步澄清,如果您有十进制值,例如 11.151.5,以上将不起作用(它们将排序为 1, 1.5, 1.15),因为那不是在 OP 中要求,但可以通过以下方式轻松完成:

SELECT [Column] FROM [Table]
ORDER BY REPLACE(RIGHT(REPLICATE('0', 1000) + LTRIM(RTRIM(CAST([Column] AS VARCHAR(MAX)))) + REPLICATE('0', 100 - CHARINDEX('.', REVERSE(LTRIM(RTRIM(CAST([Column] AS VARCHAR(MAX))))), 1)), 1000), '.', '0')

结果:1, 1.15, 1.5

而且仍然完全在 SQL 中。这不会对 IP 地址进行排序,因为您现在进入的是非常具体的数字组合,而不是简单的文本 + 数字。

【讨论】:

【参考方案4】:

RedFilter's answer 非常适合索引不重要的合理大小的数据集,但是如果您想要索引,则需要进行一些调整。

首先,将函数标记为不进行任何数据访问并且具有确定性和精确性:

[SqlFunction(DataAccess = DataAccessKind.None,
                          SystemDataAccess = SystemDataAccessKind.None,
                          IsDeterministic = true, IsPrecise = true)]

其次,MSSQL 对索引键大小有 900 字节的限制,因此如果归化值是索引中的唯一值,则它的长度必须最多为 450 个字符。如果索引包含多列,则返回值必须更小。两个变化:

CREATE FUNCTION Naturalize(@str AS nvarchar(max)) RETURNS nvarchar(450)
    EXTERNAL NAME ClrExtensions.Util.Naturalize

在 C# 代码中:

const int maxLength = 450;

最后,您需要在表中添加一个计算列,并且它必须被持久化(因为 MSSQL 无法证明 Naturalize 是确定性和精确的),这意味着归化值实际上存储在表中,但是仍然自动维护:

ALTER TABLE YourTable ADD nameNaturalized AS dbo.Naturalize(name) PERSISTED

您现在可以创建索引了!

CREATE INDEX idx_YourTable_n ON YourTable (nameNaturalized)

我还对 RedFilter 的代码进行了一些更改:为了清晰起见,使用 chars,将重复空间删除合并到主循环中,一旦结果超过限制就退出,设置不带子字符串的最大长度等。这是结果:

using System.Data.SqlTypes;
using System.Text;
using Microsoft.SqlServer.Server;

public static class Util

    [SqlFunction(DataAccess = DataAccessKind.None, SystemDataAccess = SystemDataAccessKind.None, IsDeterministic = true, IsPrecise = true)]
    public static SqlString Naturalize(string str)
    
        if (string.IsNullOrEmpty(str))
            return str;

        const int maxLength = 450;
        const int padLength = 15;

        bool isDecimal = false;
        bool wasSpace = false;
        int numStart = 0;
        int numLength = 0;

        var sb = new StringBuilder();
        for (var i = 0; i < str.Length; i++)
        
            char c = str[i];
            if (c >= '0' && c <= '9')
            
                if (numLength == 0)
                    numStart = i;
                numLength++;
            
            else
            
                if (numLength > 0)
                
                    sb.Append(pad(str.Substring(numStart, numLength), isDecimal, padLength));
                    numLength = 0;
                
                if (c != ' ' || !wasSpace)
                    sb.Append(c);
                isDecimal = c == '.';
                if (sb.Length > maxLength)
                    break;
            
            wasSpace = c == ' ';
        
        if (numLength > 0)
            sb.Append(pad(str.Substring(numStart, numLength), isDecimal, padLength));

        if (sb.Length > maxLength)
            sb.Length = maxLength;
        return sb.ToString();
    

    private static string pad(string num, bool isDecimal, int padLength)
    
        return isDecimal ? num.PadRight(padLength, '0') : num.PadLeft(padLength, '0');
    

【讨论】:

+1 对@RedFilter 答案的这些改进。另外,请参阅我对问题(上面)留下的关于支持我的建议(这里:***.com/questions/34509/…)的评论,将其作为排序选项内置到 SQL Server 中:-)。谢谢! 我来晚了,但这些都是很大的改进,谢谢! +1【参考方案5】:

这是为 SQL 2000 编写的解决方案。它可能会针对较新的 SQL 版本进行改进。

/**
 * Returns a string formatted for natural sorting. This function is very useful when having to sort alpha-numeric strings.
 *
 * @author Alexandre Potvin Latreille (plalx)
 * @param nvarchar(4000) string The formatted string.
 * @param int numberLength The length each number should have (including padding). This should be the length of the longest number. Defaults to 10.
 * @param char(50) sameOrderChars A list of characters that should have the same order. Ex: '.-/'. Defaults to empty string.
 *
 * @return nvarchar(4000) A string for natural sorting.
 * Example of use: 
 * 
 *      SELECT Name FROM TableA ORDER BY Name
 *  TableA (unordered)              TableA (ordered)
 *  ------------                    ------------
 *  ID  Name                        ID  Name
 *  1.  A1.                         1.  A1-1.       
 *  2.  A1-1.                       2.  A1.
 *  3.  R1             -->          3.  R1
 *  4.  R11                         4.  R11
 *  5.  R2                          5.  R2
 *
 *  
 *  As we can see, humans would expect A1., A1-1., R1, R2, R11 but that's not how SQL is sorting it.
 *  We can use this function to fix this.
 *
 *      SELECT Name FROM TableA ORDER BY dbo.udf_NaturalSortFormat(Name, default, '.-')
 *  TableA (unordered)              TableA (ordered)
 *  ------------                    ------------
 *  ID  Name                        ID  Name
 *  1.  A1.                         1.  A1.     
 *  2.  A1-1.                       2.  A1-1.
 *  3.  R1              -->         3.  R1
 *  4.  R11                         4.  R2
 *  5.  R2                          5.  R11
 */
ALTER FUNCTION [dbo].[udf_NaturalSortFormat](
    @string nvarchar(4000),
    @numberLength int = 10,
    @sameOrderChars char(50) = ''
)
RETURNS varchar(4000)
AS
BEGIN
    DECLARE @sortString varchar(4000),
        @numStartIndex int,
        @numEndIndex int,
        @padLength int,
        @totalPadLength int,
        @i int,
        @sameOrderCharsLen int;

    SELECT 
        @totalPadLength = 0,
        @string = RTRIM(LTRIM(@string)),
        @sortString = @string,
        @numStartIndex = PATINDEX('%[0-9]%', @string),
        @numEndIndex = 0,
        @i = 1,
        @sameOrderCharsLen = LEN(@sameOrderChars);

    -- Replace all char that have the same order by a space.
    WHILE (@i <= @sameOrderCharsLen)
    BEGIN
        SET @sortString = REPLACE(@sortString, SUBSTRING(@sameOrderChars, @i, 1), ' ');
        SET @i = @i + 1;
    END

    -- Pad numbers with zeros.
    WHILE (@numStartIndex <> 0)
    BEGIN
        SET @numStartIndex = @numStartIndex + @numEndIndex;
        SET @numEndIndex = @numStartIndex;

        WHILE(PATINDEX('[0-9]', SUBSTRING(@string, @numEndIndex, 1)) = 1)
        BEGIN
            SET @numEndIndex = @numEndIndex + 1;
        END

        SET @numEndIndex = @numEndIndex - 1;

        SET @padLength = @numberLength - (@numEndIndex + 1 - @numStartIndex);

        IF @padLength < 0
        BEGIN
            SET @padLength = 0;
        END

        SET @sortString = STUFF(
            @sortString,
            @numStartIndex + @totalPadLength,
            0,
            REPLICATE('0', @padLength)
        );

        SET @totalPadLength = @totalPadLength + @padLength;
        SET @numStartIndex = PATINDEX('%[0-9]%', RIGHT(@string, LEN(@string) - @numEndIndex));
    END

    RETURN @sortString;
END

【讨论】:

【参考方案6】:

我知道此时这有点老了,但在我寻找更好的解决方案时,我遇到了这个问题。我目前正在使用一个功能来订购。它可以很好地用于排序以混合字母数字命名的记录('item 1'、'item 10'、'item 2'等)

CREATE FUNCTION [dbo].[fnMixSort]
(
    @ColValue NVARCHAR(255)
)
RETURNS NVARCHAR(1000)
AS

BEGIN
    DECLARE @p1 NVARCHAR(255),
        @p2 NVARCHAR(255),
        @p3 NVARCHAR(255),
        @p4 NVARCHAR(255),
        @Index TINYINT

    IF @ColValue LIKE '[a-z]%'
        SELECT  @Index = PATINDEX('%[0-9]%', @ColValue),
            @p1 = LEFT(CASE WHEN @Index = 0 THEN @ColValue ELSE LEFT(@ColValue, @Index - 1) END + REPLICATE(' ', 255), 255),
            @ColValue = CASE WHEN @Index = 0 THEN '' ELSE SUBSTRING(@ColValue, @Index, 255) END
    ELSE
        SELECT  @p1 = REPLICATE(' ', 255)

    SELECT  @Index = PATINDEX('%[^0-9]%', @ColValue)

    IF @Index = 0
        SELECT  @p2 = RIGHT(REPLICATE(' ', 255) + @ColValue, 255),
            @ColValue = ''
    ELSE
        SELECT  @p2 = RIGHT(REPLICATE(' ', 255) + LEFT(@ColValue, @Index - 1), 255),
            @ColValue = SUBSTRING(@ColValue, @Index, 255)

    SELECT  @Index = PATINDEX('%[0-9,a-z]%', @ColValue)

    IF @Index = 0
        SELECT  @p3 = REPLICATE(' ', 255)
    ELSE
        SELECT  @p3 = LEFT(REPLICATE(' ', 255) + LEFT(@ColValue, @Index - 1), 255),
            @ColValue = SUBSTRING(@ColValue, @Index, 255)

    IF PATINDEX('%[^0-9]%', @ColValue) = 0
        SELECT  @p4 = RIGHT(REPLICATE(' ', 255) + @ColValue, 255)
    ELSE
        SELECT  @p4 = LEFT(@ColValue + REPLICATE(' ', 255), 255)

    RETURN  @p1 + @p2 + @p3 + @p4

END

然后调用

select item_name from my_table order by fnMixSort(item_name)

它很容易将简单数据读取的处理时间增加三倍,因此它可能不是完美的解决方案。

【讨论】:

【参考方案7】:

这是我喜欢的另一个解决方案: http://www.dreamchain.com/sql-and-alpha-numeric-sort-order/

这不是 Microsoft SQL,但由于我在为 Postgres 寻找解决方案时最终来到了这里,所以我认为在此处添加它会对其他人有所帮助。

编辑:这是代码,以防链接消失。

CREATE or REPLACE FUNCTION pad_numbers(text) RETURNS text AS $$
  SELECT regexp_replace(regexp_replace(regexp_replace(regexp_replace(($1 collate "C"),
    E'(^|\\D)(\\d1,3($|\\D))', E'\\1000\\2', 'g'),
      E'(^|\\D)(\\d4,6($|\\D))', E'\\1000\\2', 'g'),
        E'(^|\\D)(\\d7($|\\D))', E'\\100\\2', 'g'),
          E'(^|\\D)(\\d8($|\\D))', E'\\10\\2', 'g');
$$ LANGUAGE SQL;

"C" 是 postgresql 中的默认排序规则;你可以指定任何你想要的排序规则,或者如果你可以确定你的表列永远不会分配一个不确定的排序规则,则删除排序规则语句。

用法:

SELECT * FROM wtf w 
  WHERE TRUE
  ORDER BY pad_numbers(w.my_alphanumeric_field)

【讨论】:

2021,这个便宜又令人愉快的解决方案仍然比我尝试过的其他解决方案更能满足我的需求。注意:如果您的列排序规则是不确定的,那么您需要在函数中明确指定排序规则。【参考方案8】:

对于以下varchar数据:

BR1
BR2
External Location
IR1
IR2
IR3
IR4
IR5
IR6
IR7
IR8
IR9
IR10
IR11
IR12
IR13
IR14
IR16
IR17
IR15
VCR

这对我来说效果最好:

ORDER BY substring(fieldName, 1, 1), LEN(fieldName)

【讨论】:

【参考方案9】:

如果您在从数据库加载数据以在 C# 中排序时遇到问题,那么我相信您会对在数据库中以编程方式执行此操作的任何方法感到失望。当服务器要排序时,它必须像你一样计算“感知”顺序——每次。

我建议您在首次插入数据时使用某些 C# 方法添加一个额外的列来存储预处理的可排序字符串。例如,您可能会尝试将数字转换为固定宽度范围,因此“xyz1”会变成“xyz00000001”。然后你就可以使用普通的 SQL Server 排序了。

冒着自吹自擂的风险,我写了一篇 CodeProject 文章来实现 CodingHorror 文章中提出的问题。欢迎随时steal from my code。

【讨论】:

【参考方案10】:

简单的排序

ORDER BY 
cast (substring(name,(PATINDEX('%[0-9]%',name)),len(name))as int)

 ##

【讨论】:

【参考方案11】:

我刚刚在某处读过一篇关于此类主题的文章。关键是:你只需要整数值来排序数据,而'rec'字符串属于UI。您可以将信息拆分为两个字段,例如 alpha 和 num,按 alpha 和 num(分别)排序,然后显示由 alpha + num 组成的字符串。您可以使用计算列来组成字符串或视图。 希望对你有帮助

【讨论】:

【参考方案12】:

您可以使用以下代码解决问题:

Select *, 
    substring(Cote,1,len(Cote) - Len(RIGHT(Cote, LEN(Cote) - PATINDEX('%[0-9]%', Cote)+1)))alpha,
    CAST(RIGHT(Cote, LEN(Cote) - PATINDEX('%[0-9]%', Cote)+1) AS INT)intv 
FROM Documents 
   left outer join Sites ON Sites.IDSite = Documents.IDSite 
Order BY alpha, intv

问候, rabihkahaleh@hotmail.com

【讨论】:

【参考方案13】:

我像往常一样迟到了。尽管如此,这是我尝试的一个似乎效果很好的答案(我会这么说)。它假定文本末尾带有数字,就像在原始示例数据中一样。

首先是一个不会很快赢得“漂亮 SQL”竞赛的函数。

CREATE FUNCTION udfAlphaNumericSortHelper (
@string varchar(max)
)
RETURNS @results TABLE (
    txt varchar(max),
    num float
)
AS
BEGIN

  DECLARE @txt varchar(max) = @string
  DECLARE @numStr varchar(max) = ''
  DECLARE @num float = 0
  DECLARE @lastChar varchar(1) = ''

  set @lastChar = RIGHT(@txt, 1)
  WHILE @lastChar <> '' and @lastChar is not null
  BEGIN 
    IF ISNUMERIC(@lastChar) = 1
    BEGIN 
        set @numStr = @lastChar + @numStr
        set @txt = Substring(@txt, 0, len(@txt))
        set @lastChar = RIGHT(@txt, 1)
    END
    ELSE
    BEGIN 
        set @lastChar = null
    END
  END
  SET @num = CAST(@numStr as float)

  INSERT INTO @results select @txt, @num
  RETURN;
END

然后像下面这样调用它:

declare @str nvarchar(250) = 'sox,fox,jen1,Jen0,jen15,jen02,jen0004,fox00,rec1,rec10,jen3,rec14,rec2,rec20,rec3,rec4,zip1,zip1.32,zip1.33,zip1.3,TT0001,TT01,TT002'


SELECT tbl.value  --, sorter.txt, sorter.num
FROM STRING_SPLIT(@str, ',') as tbl
CROSS APPLY dbo.udfAlphaNumericSortHelper(value) as sorter
ORDER BY sorter.txt, sorter.num, len(tbl.value)

结果: 狐狸 狐狸00 仁0 詹1 詹02 詹3 詹0004 詹15 推荐1 记录2 记录3 REC4 推荐10 推荐14 REC20 短袜 TT01 TT0001 TT002 zip1 zip1.3 zip1.32 zip1.33

【讨论】:

【参考方案14】:

我还是不明白(可能是因为我的英语不好)。

你可以试试:

ROW_NUMBER() OVER (ORDER BY dbo.human_sort(field_name) ASC)

但它不适用于数百万条记录。

这就是为什么我建议使用 填充 分隔列的触发器 human value

此外:

内置的 T-SQL 函数真的是 慢,微软建议使用 .NET 函数。 人的价值是恒定的,所以每次都计算它没有意义 查询运行时。

【讨论】:

不幸的是,T-SQL 中没有human_sort。所以我认为您建议将 C# 函数添加到 SQL 中。有人知道在那里使用的好功能吗?我知道的所有机制(包括该帖子中的杰夫)比较两个值,而不是返回一个值进行常规排序。有谁知道更好的 T-SQL(甚至更好的普通 SQL:92 或 2003 标准)方法来做到这一点? 查看我的答案 - 它提供了一个 CLR 函数,该函数返回一个可以排序的标量。它将大大优于任何 T-SQL 解决方案。

以上是关于Microsoft SQL 2005 中的自然(人类字母数字)排序的主要内容,如果未能解决你的问题,请参考以下文章

Microsoft Sql server2005的安装步骤和常见问题解决方案

从 Microsoft Access 2007 迁移到 Sql Server 2005

Microsoft SQL Server 2005 函数,传递开始和结束时间列表

VS2010中连接sql 2005连接问题microsoft.sqlserver.management.sdk.sfc

问:卸载microsoft sql server 2005 backward compatibility的方法

Microsoft SQL Server 2005 从返回多个表的子过程插入表