SQL与LINQ和.NET中的AlphaNumeric排序

时间:2009-11-24 19:01:01

标签: sql linq linq-to-objects sql-order-by

我今天遇到了一件有趣的事情,我以前从未注意过。似乎SQL和LINQ以不同的方式对AlphaNumeric字符串进行排序。

数据表包含行: 一个 G 6 P 1 d Ĵ 2 Ť ž 9 F 0

如果我在SQL中执行Order By,我会收到以下结果: 一个 d F G Ĵ P Ť ž 0 1 2 6 9

现在考虑一下这个LINQ样本:

class Program
{
    static void Main(string[] args)
    {
        var list = new List<string>()
                       {
                           "A",
                           "G",
                           "6",
                           "P",
                           "1",
                           "D",
                           "J",
                           "2",
                           "T",
                           "Z",
                           "9",
                           "F",
                           "0"
                       };
        Console.WriteLine("Default Order:");
        list.ForEach(s => Console.WriteLine(s));

        Console.WriteLine();
        Console.WriteLine("Sorted Order:");
        foreach (string s in list.OrderBy(f => f))
        {
            Console.WriteLine(s);
        }                        
    }
}

这个的输出是 0 1 2 6 9 一个 d F G Ĵ P Ť ž

因此,SQL将字母放在第一位且第二位是数字,LINQ首先订购数字,第二订购字母。我将这些结果放在DataGrid中并单击了标题,当然它也命令了一个LINQ,所以这可能是一个更深层次的鸿沟,就像在.NET / Windows级别一样。

我遇到的问题是我的用户期望他们习惯看到SQL排序的结果。如何使LINQ以相同的方式运行?

更新

下面标出了答案,但是对于那些后来偶然发现的人,我想回顾一下,因为它采用了一系列答案来解决问题。

1)dcp立即进入了我的大脑没有的地方:数据源。问题是使用EBCDIC排序的IBM SQL与使用ASCII排序的已知Universe中的所有其他技术之间的区别。感谢IBM再一次提醒我为什么我现在是.NET开发人员。

2)认识到这一点,艾哈迈德提供了一个非常优雅的解决方案,也让我望而却步:自定义IComparer&lt; string&gt;。我使用了他提供的代码,并根据需要对List进行了排序。

感谢StackOverflow再次出现!

更新2

昨天发布后,我完成了这项工作并希望分享最终结果。

昨天的帖子是一个简单的单个字符列表,但实际上它们嵌入了更长的字符串中。为了使这个工作更长的字符串,我将原始的字符串比较器更改为字符比较器,然后循环遍历字符串并比较每个字符,直到我找到不匹配或用完字符进行比较。以下是最后两个Comparer类:

public class EbcdicCharComparer:IComparer {     public int Compare(char x,char y)     {         int xNum,yNum;         bool xIsNum = Int32.TryParse(x.ToString(),out xNum);         bool yIsNum = Int32.TryParse(y.ToString(),out yNum);

    // compare numbers
    if (xIsNum && yIsNum)
    {
        return xNum.CompareTo(yNum);
    }

    // compare num to char
    if (xIsNum)
    {
        return 1;
    }

    // compare num to char
    if (yIsNum)
    {
        return -1;
    }

    // compare normally
    return x.CompareTo(y);

}

}

public class EbcdicStringComparer:IComparer {     public int Compare(string x,string y)     {         var xArr = x.ToCharArray();         var yArr = y.ToCharArray();

    var iterations = xArr.Length > yArr.Length ? yArr.Length : xArr.Length;
    var charComp = new EbcdicCharComparer();
    for (int i = 0; i < iterations; i++)
    {
        var compValue = charComp.Compare(xArr[i], yArr[i]);
        if (compValue != 0)
            return compValue;
    }

    // compare as strings
    return x.CompareTo(y);
}

}

真实数据更像是这样:

  • 024 A 17
  • 024 A 18
  • 024 A 19
  • 024 1 19
  • 024 C 19A
  • 024 3 3
  • 024 A 3B

根据需要,比较器现在按以下顺序返回数据:

  • 024 A 3B
  • 024 A 17
  • 024 A 18
  • 024 A 19
  • 024 C 19A
  • 024 1 19
  • 024 3 3

再次感谢所有帮助过的人。

2 个答案:

答案 0 :(得分:2)

您可以使用接受IComparer的overloaded OrderBy method。 IComparer会比较项目以产生所需的结果:

public class CustomComparer : IComparer<string>
{
    public int Compare(string x, string y)
    {
        double xNum, yNum;
        bool xIsNum = Double.TryParse(x, out xNum);
        bool yIsNum = Double.TryParse(y, out yNum);

        // compare numbers
        if (xIsNum && yIsNum)
        {
            return xNum.CompareTo(yNum);
        }

        // compare num to string
        if (xIsNum)
        {
            return 1;
        }

        // compare num to string
        if (yIsNum)
        {
            return -1;
        }

        // compare as strings
        return x.CompareTo(y);
    }
}

用法:

list.OrderBy(i => i, new CustomComparer())

实现可能已使用Int32.TryParse来表示您的特定示例数据,但我使用了Double以防您在列表中有“3.0”等。当然,您需要了解您的数据。如果值太大,您将需要使用适当的TryParse方法。

答案 1 :(得分:1)

在我尝试过的数据库(Oracle和SQL Server 2008)上,它使用ASCII排序(字母前的数字与LINQ查询相同)进行排序。你在使用其他数据库吗?

SQL Server查询:

SELECT 'A' col1 
UNION ALL
SELECT 'B' col1 
UNION ALL
SELECT '0' col1 
ORDER BY 1

行返回0,A,B

Oracle Query:

SELECT 'A' col1 FROM dual
UNION ALL
SELECT 'B' col1 FROM dual
UNION ALL
SELECT '0' col1 FROM dual
ORDER BY 1

行返回0,A,B