有没有办法加速大数据集的LINQ查询中的包含扩展?

时间:2014-07-15 01:52:50

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

感谢您的光临。

背景

我有一个C#方法,允许用户传入搜索和过滤器对象。然后,该方法通过实体框架将FULLTEXT函数作为IQueryable调用。然后根据过滤器的约束进一步突变相同的IQueryable并最终枚举。这很好用,这是一个伪代码示例:

var db = MyDataContext();

IQueryable<Foo> results = db.QueryAllMyFoos();

results = db.SomeFullTextFunctionInTheDatabase("buzz");

. . .

return results.Skip(0).Take(10); //etc. etc.

问题

我被要求必须保存搜索并在结果集中启用搜索。

我认为通过存储表示第一次搜索的唯一ID的整数数组,然后使用.Contains()(再次,伪代码)将它们传递到第二次搜索的谓词中,可以轻松解决这个问题:

var firstSearchResults = int[]{50,63,123. . . .456788};

IQueryable<Foo> results = db.QueryAllMyFoos();

results = db.SomeFullTextFunctionInTheDatabase("fizz").Where(w => firstSearchResults.Contains(w.Id));

嗯,它有效但是考虑到firstSearchResults通常可能包含超过20,000个唯一ID,这是非常缓慢的。

我尝试了什么

当然,我尝试了上面提到的Contains()逻辑和先前请求的缓存数组。

其次,我尝试在SQL中创建一个数据表,以保存已保存搜索的ID,每个结果ID都在其自己的行中。这对原始问题没有任何帮助。

接下来,我阅读了C#HashSets并尝试将firstSearchResults转换为HashSet,然后在.Contains()扩展程序中使用它。这似乎有所帮助,但还不够,因为我的一些搜索仍然需要4-5分钟才能返回结果。

我也试过.Join()但编译器抱怨我无法在return语句中重构我的对象实例。

我查看了.Intersect,但这需要我在firstSearchResultsresults IQueryable之间创建一个新的交叉ID列表。然后,我必须列举整个事情来获得最终的结果列表。由于我正在分页结果集,因此我需要保持results的可查询性,直到orderskiptake为止。

最后,出于绝望,我开始编写一个SQL存储过程,在其中我将datatable个id作为READONLY参数传递,然后使用我的关键字函数的结果进行连接运行全文搜索。这可能有用,但SQL不是我的强项,我暂时放弃了追求这个帖子。

感谢任何帮助。

1 个答案:

答案 0 :(得分:0)

.Intersect()似乎在性能方面提供了最佳结果。

从算法上讲:

  1. 让您的IQueryable完全变异,然后从中选择一个唯一ID列表
  2. 将该列表与您希望限制搜索的超大ID列表相交
  3. 您现在拥有一系列有效的ID,可以随搜索一起返回。分页中的因素(.Skip(n).Take(n)),您可以快速构建返回对象的子集。
  4. 这是一个伪代码示例:

        //current result set
        var resultIds = iQueryableResults.Select(s => s.Id).ToArray(); 
    
        //intersect with some other array of ints
        var intersection = resultIds.Intersect(priorResults).ToArray(); 
    
        //Total the results in the intersection
        var resultCount = intersection.Count();
    
        //Get an upper boundary for your loop.             
        var upperLimit = (resultCount >= ((filter.Skip + filter.Take) - 1))
                                         ? (filter.Skip + filter.Take) - 1
                                         : resultCount;
    
        var results = new List<MyReturnObject>();
    
        //Back to the basics
        for (int i = filter.Skip; i < upperLimit; i++)
        {
               var _id = intersection[i];
               results.Add(QueryAllMyObjects().FirstOrDefault(f => f.Id == _id));
        }
    
        return results;
    

    这不是优雅或漂亮的,但是它比在.Contains()谓词中使用where要快得多(与大约20-30k的id数组进行比较时大约需要2-3秒) IDS)。希望它可以帮助别人。