【问题标题】:T-SQL IsNumeric() and Linq-to-SQLT-SQL IsNumeric() 和 Linq-to-SQL
【发布时间】:2011-01-20 17:41:06
【问题描述】:

我需要从数据库中找到满足特定格式约定的最高值。具体来说,我想找到看起来像的最高值

EU999999('9' 是任意数字)

select max(col) 将返回类似 'EUZ...' 的内容,例如我想排除的内容。 以下查询可以解决问题,但我无法通过 Linq-to-SQL 生成它。 SQL Server 中的 isnumeric() 函数似乎没有翻译。

select max(col) from table where col like 'EU%' 
    and 1=isnumeric(replace(col, 'EU', ''))

编写数据库函数、存储过程或任何其他类似性质的东西在我的首选解决方案列表中远远低于,因为这个表是我的应用程序的核心,我不能轻易地用其他东西替换表对象。

下一个最佳解决方案是什么?

【问题讨论】:

    标签: sql-server linq-to-sql tsql isnumeric


    【解决方案1】:

    正如您所说,IsNumeric 没有从 LINQ 到 SQL 的翻译。有几个选项,你已经写了数据库函数和存储过程。我想再添加两个。

    选项 1:您可以通过将 LINQ to SQL 与 LINQ to Objects 混合使用来做到这一点,但是当您拥有大型数据库时,不要期望获得出色的性能:

    var cols = (from c in db.Table where c.StartsWith("EU") select c).ToList();
    var stripped = from c in cols select int.Parse(c.Replace("EU", ""));
    var max = stripped.Max();
    

    选项 2:更改您的数据库架构 :-)

    【讨论】:

    • 仍然,+1 建议更改我的架构。
    【解决方案2】:

    我的建议是退回到内联 SQL 并使用 DataContext.ExecuteQuery() 方法。您将使用您在开始时发布的 SQL 查询。

    这是我在类似情况下所做的。由于缺乏类型检查和可能的语法错误,这并不理想,但只需确保它包含在任何单元测试中。并非所有可能的查询都包含在 Linq 语法中,因此首先存在 ExecuteQuery。

    【讨论】:

      【解决方案3】:

      您可以通过向 DataContext 的分部类添加方法来使用 ISNUMERIC 函数。这类似于使用 UDF。

      在 DataContext 的部分类中添加:

      partial class MyDataContext
      {
          [Function(Name = "ISNUMERIC", IsComposable = true)]
          public int IsNumeric(string input)
          {
              throw new NotImplementedException(); // this won't get called
          }
      }
      

      那么你的代码会以这种方式使用它:

      var query = dc.TableName
                    .Select(p => new { p.Col, ReplacedText = p.Col.Replace("EU", "") })
                    .Where(p => SqlMethods.Like(p.Col, "EU%")
                              && dc.IsNumeric(p.ReplacedText) == 1)
                    .OrderByDescending(p => p.ReplacedText)
                    .First()
                    .Col;
      
      Console.WriteLine(query);
      

      或者你可以使用 MAX:

      var query = dc.TableName
                    .Select(p => new { p.Col, ReplacedText = p.Col.Replace("EU", "") })
                    .Where(p => SqlMethods.Like(p.Col, "EU%")
                        && dc.IsNumeric(p.ReplacedText) == 1);
      
      var result = query.Where(p => p.ReplacedText == query.Max(p => p.ReplacedText))
                        .First()
                        .Col;
      
      Console.WriteLine("Max: {0}, Result: {1}", max, result);
      

      根据您的最终目标,可能会停在max 变量处,并在其前面加上“EU”文本,以避免获取列名的第二个查询。

      编辑:正如 cmets 中所述,这种方法的缺点是排序是基于文本而不是数值完成的,并且目前在 SQL 上没有Int32.Parse 的翻译。

      【讨论】:

      • +1。我很惊讶这是可能的。不要忘记排序 OrderByDescending(p => p.ReplacedText) 是基于文本的(ReplacedText 是字符串/varchar),因此这可能不会产生正确的结果。在排序之前必须将其转换为整数。
      • @Steven 谢谢,这是一个简单功能的巧妙解决方法。你是对的订单。不幸的是,没有解决方法,Int32.Parse 没有转换为 SQL,因此对于数字排序,最好使用真正的 UDF/SPROC。
      • 这里也一样,这是我最近看到的最漂亮的东西之一。
      • 小缺点是如果你使用 Convert.ToInt32 它总是被评估。你可以解决这个问题: (db.IsNumeric(p.AddressPostCode) == 1 ? (int?) Convert.ToInt32(p.AddressPostCode) : null)
      【解决方案4】:

      虽然缺少ISNUMERIC,但您始终可以尝试几乎等效的NOT LIKE '%[^0-9]%,即字符串中没有非数字,或者字符串为空或仅包含数字:

      from x in table 
      where SqlMethods.Like(x.col, 'EU[0-9]%') // starts with EU and at least one digit
        && !SqlMethods.Like(x.col, '__%[^0-9]%') // and no non-digits
      select x;
      

      当然,如果你知道位数是固定的,这可以简化为

      from x in table 
      where SqlMethods.Like(x.col, 'EU[0-9][0-9][0-9][0-9][0-9][0-9]')
      select x;
      

      【讨论】:

      • 我喜欢你的建议,我会接受它,但不幸的是,我的前缀(和长度)不是常数,所以我必须动态生成表达式。我不喜欢,我正在考虑改变架构的方法,以便我可以解决这个问题。
      • 使用前面没有通配符的LIKE 的一个优点是它对索引非常友好(可以对索引进行范围检查)。
      • 一个非常聪明的替代 IsNumeric 的变长表达式。在字符串中的任意位置查找非数字字符。感谢您的提示!
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2010-12-02
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2010-11-20
      • 2017-04-15
      相关资源
      最近更新 更多