【问题标题】:Efficiently make iterative LINQ to SQL queries高效地进行迭代 LINQ to SQL 查询
【发布时间】:2017-01-11 14:44:52
【问题描述】:

我正在尝试使用 LINQ-to-SQL 实现以下算法:

给定一个字符串列表 L,返回 DB 中的每一行 R,其中 L 中的每个字符串都是 R 中列值之一的子字符串。

问题是我如何为 L 中的每个字符串迭代地执行此操作?我不知道如何巧妙地将其全部放入一个 Linq-To-SQL 语句中。请注意,我可以按照以下方式编写代码:

field1.contains(...) || field2.contains(...) || ...

因为没有那么多列。

例如,如果输入是

[“查理”、“医生”、“科尔”]

我们将输出所有包含“Charlie”作为子字符串的字段、“Doctor”作为子字符串的字段和“Kor”作为子字符串的字段的所有行。

我想到的一种方法是对每个输入值进行单独的 SQL 查询并取所有这些值的交集。

另一种方法是仅从输入中选择一个字符串,对其进行 SQL 查询,将其转换为列表,然后仅使用 C# 中的 LINQ 一次过滤掉其余字符串。

有什么想法可以做到这一点的最佳方式吗?

【问题讨论】:

  • 为什么不做一个查询,把东西放在哪里? .Where(r => r.MyField.Contains("Doctor") || r.MyField.Contains("Charlie") || ...)
  • 我认为他的意思是,他不想手动检查每一列。他正在寻找一种检查所有列的方法。不过,您对个人的建议是正确的。它会起作用的。
  • 问题不是手动检查每一列,我很高兴这样做,因为有 O(1) 列。但是,如果我的输入是一个大批?这在语法上会是什么样子?
  • 请将您最后的评论纳入问题,因为现在的方式不清楚您在问什么。
  • 我已经更新了问题。

标签: c# sql linq linq-to-sql


【解决方案1】:

我会尝试All 扩展方法(EF6 支持它,不确定 LINQ to SQL):

List<string> values = new List<string> { "Charlie", "Doctor", "Kor" };
var query = db.Table
    .Where(r => values.All(v => r.Field1.Contains(v) || r.Field2.Contains(v) || ...));

更新:好吧,假设是错误的 - 正如 cmets 中提到的,不幸的是 LINQ to SQL 不支持上述构造(对他们感到羞耻)。

像往常一样,在这种情况下,我会动态构建相应的谓词表达式。

在这种特殊情况下,我们需要这样的东西(N 字段和 M 值):

r => (r.Field1.Contains(value1) || r.Field2.Contains(value1) ... || r.FieldN.Contains(value1))
  && (r.Field1.Contains(value2) || r.Field2.Contains(value2) ... || r.FieldN.Contains(value2))
  ...
  && (r.Field1.Contains(valueM) || r.Field2.Contains(valueM) ... || r.FieldN.Contains(valueM));

这是一个自定义扩展方法:

public static class QueryableExtensions
{
    public static IQueryable<T> WhereContainsAll<T>(
        this IQueryable<T> source, 
        IEnumerable<string> values,
        params Expression<Func<T, string>>[] members)
    {
        var parameter = Expression.Parameter(typeof(T), "r");
        var body = values
            .Select(value => members
                .Select(member => (Expression)Expression.Call(
                    Expression.MakeMemberAccess(parameter, ((MemberExpression)member.Body).Member),
                    "Contains", Type.EmptyTypes, Expression.Constant(value)))
                .Aggregate(Expression.OrElse))
            .Aggregate(Expression.AndAlso);
        var predicate = Expression.Lambda<Func<T, bool>>(body, parameter);
        return source.Where(predicate);
    }
}

示例用法是

List<string> values = new List<string> { "Charlie", "Doctor", "Kor" };
var query = db.Table.WhereContainsAll(values,
    r => r.Field1, r => r.Field2, r => r.Field3, ...);

这应该会导致 IMO 应该是最佳的单个 SQL 查询,因为繁重的工作将由数据库引擎完成。当然,查询很可能会导致全表扫描,但即使使用单个 Contains (SQL LIKE) 条件也会发生同样的情况。

【讨论】:

  • 好主意,但不幸的是,我得到了 NotSupportedException:本地序列不能在查询运算符的 LINQ to SQL 实现中使用,除了包含运算符。
  • 啊,太糟糕了 :( 但只是为了确认一下,这就是你要找的东西,只需要找到一种让它工作的方法吗?
  • 是的。目标是最大限度地提高效率,我的假设是我为 SQL 与 C# 后处理做的工作越多,效果就会越好。
  • 动态生成复杂表达式的好例子-
【解决方案2】:

试试这个(我用列表做了一个例子):

var dbValues = new List<string> {"hello", "how", "are", "you"};
var substrings = new List<string> {"ello", "re"};

var result = dbValues.Where(i => substrings.Any(l => i.Contains(l))).ToList();

结果将包含 {"hello","are"}

数据库示例:

using (var db = new MyDatabase())
{
    var substrings = new List<string> { "ello", "re" };
    var result = db.MyTable.Where(i => substrings.Any(l => i.Value.Contains(l))).ToList();
}

【讨论】:

  • 查询提供者不知道如何将其转换为 SQL。问题不在于查询内存列表;它询问的是查询数据库。
  • 好的,如果有人在将该逻辑转换为 SQL 时遇到问题,这里是: using (var db = new DB()) { var substrings = new List { "ello", "回覆” }; var 结果 = db.MyTable.Where(i => substrings.Any(l => i.Value.Contains(l))).ToList(); }
  • 这不是 SQL...这只是一个运行时会出错的 LINQ 查询,因为查询提供程序将无法将其转换为 SQL,正如我在第一条评论中提到的那样。
  • 我知道那不是生成的 SQL,但是当你运行这个时,你可以使用 profiler 捕获生成的 SQL。
  • 不,当您运行它时,您会看到程序崩溃,因为查询提供程序无法将其转换为 SQL,就像我已经告诉过您两次一样。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2010-12-17
  • 1970-01-01
相关资源
最近更新 更多