自定义 T-SQL 排序依据 (IComparer)

Custom T-SQL Order By (IComparer)

T-SQL 是否有能力对字符串进行自定义比较(用于排序),等同于 .NET 的 IComparer

比如能够给 Order By 一个用户定义的函数,它接受 2 个字符串和 returns 一个表示它们如何比较的值(大于、小于、等于)?

我目前有一个 C# ICompararer 实现用于对代码中的内容进行排序,但现在我需要从存储过程中生成相同的排序输出。

供参考,这是我试图在 TSQL 中实现的 IComparer

public class SemanticComparer : IComparer<string>
{
    private static Regex _splitter = new Regex("\W+");

    public int Compare(string x, string y)
    {
        string[] partsX = _splitter.Split(x);
        string[] partsY = _splitter.Split(y);

        int shortest = Math.Min(partsX.Length, partsY.Length);

        for (int index = 0; index < shortest; index++)
        {
            int intX, intY;
            int result;

            if (int.TryParse(partsX[index], out intX) && int.TryParse(partsY[index], out intY))
            {
                result = intX.CompareTo(intY);
            }
            else
            {
                result = string.Compare(partsX[index], partsY[index], StringComparison.Ordinal);
            }

            if (result != 0)
            {
                return result;
            }
        }

        return 0;
    }
}

它需要能够对看起来像这样的东西进行排序(按照它们应该输出的顺序):

其中每个非单词字符将字符串拆分为多个段,如果可能则尝试对它们进行数字比较,如果不能则作为字符串进行比较。段数可以任意"depth"。

不幸的是,CLR 存储过程不是一个选项。

with data as (
    select c from (values
        ('9.1'), ('9.2'), ('9.2.1'), ('9.02.2'), ('9.02.3'), ('9.3'), ('9.3.1'), ('9.3.2'),
        ('10.'), ('11.'), ('11.1'), ('11.2.a'), ('11.2.b'), ('11.2.c'), ('11a.2.a'), ('11b.2.b')
    ) t(c)
)
select c, '[' +
    right('00000' +
        substring(c, 1, charindex('.', c + '.0.0', 1) - 1) +
            case when substring(c + '.0.0', charindex('.', c + '.0.0', 1) - 1, 1) between '0' and '9' then ' ' else '' end,
        6
    ) + '.' +
    right('00000' +
        substring(c, charindex('.', c + '.0.0', 1) + 1, charindex('.', c + '.0.0', charindex('.', c + '.0.0', 1) + 1) - charindex('.', c + '.0.0', 1) - 1) +
            case when right('0' +
                substring(c, charindex('.', c + '.0.0', 1) + 1, charindex('.', c + '.0.0', charindex('.', c + '.0.0', 1) + 1) - charindex('.', c + '.0.0', 1) - 1),
                1
            ) between '0' and '9' then ' ' else '' end,
        6
    ) + '.' +
    right('00000' +
        substring(c, charindex('.', c + '.0.0', charindex('.', c + '.0.0', 1) + 1) + 1, 10) +
            case when right('0' +
                substring(c, charindex('.', c + '.0.0', charindex('.', c + '.0.0', 1) + 1) + 1, 10),
                1
            ) between '0' and '9' then ' ' else '' end,
        6
    ) + ']'
from data
order by 2;

我把它放在一起是为了好玩。如您所见,SQL 中的字符串解析通常工作量很大,并且有很多重复的子表达式。

这里的想法是,您似乎可以将值转换为规范化格式, 可通过常规字母排序进行排序。前面的数字部分是zero-padded。每个 "field" 的末尾允许有一个字母字符,否则会附加一个 space。举个例子 11.2.a 变成 [00011 .00002 .00000a]

它相当灵活,您当然可以将此逻辑包装在一个标量函数中。我会让你决定这整件事是不是个好主意。

http://rextester.com/KZX77690

http://rextester.com/LYL6977(略有改善?)