有效加入内存列表

时间:2013-09-07 19:19:49

标签: c# sql performance linq entity-framework

在EF中,如果我有一个灵长类动物列表(列表),那么对表的“加入”很容易:

var ids = int[]{1,4,6}; //some random values
var rows = context.SomeTable.Where(r => ids.Contains(r.id))

在您希望加入多个列的瞬间,这会变得更加复杂:

var keys = something.Select(s => new { s.Field1, s.Field2 })
var rows = context.SomeTable.Where(r => keys.Contains(r => new { s.Field1, s.Field2 })); // this won't work

我找到了两种加入它的方法,但两者都不是很好:

  1. 吮吸整个表格,并根据其他数据对其进行过滤。 (如果表格非常大,这会变慢)
  2. 对于每个键,查询表(如果你有相当数量的行可以拉慢这个速度)
  3. 有时,我能够做出的妥协是修改后的#1:基于一个相当独特的密钥拉入表的子集

    var keys = something.Select(s => s.Field1)
    var rows = context.SomeTable.Where(r => keys.Contains(s.Field1)).ToList();
    foreach (var sRow in something)
    {
        var joinResult = rows.Where(r => r.Field1 == sRow.Field1 && r.Field2 == sRow.Field2);
        //do stuff
    }
    

    但即使这样也可以撤回太多数据。

    我知道有一些方法可以将表值参数引入ADO.Net,以及我可以构建一系列OR一起的.Where()子句的方法。有没有人有魔法子弹?

4 个答案:

答案 0 :(得分:0)

您可以尝试展平您的密钥,然后使用相同的Contains模式。虽然您可以使用函数索引将展平的密钥存储在数据库中,但这对大型查询可能不会很好...

我使用列K1 int, K2 int, Name varchar(50)

进行表格测试
var l = new List<Tuple<int, int>>();
l.Add(new Tuple<int, int>(1, 1));
l.Add(new Tuple<int, int>(1, 2));
var s = l.Select(k => k.Item1.ToString() + "," + k.Item2.ToString());
var q = Tests.Where(t => s.Contains(t.K1.ToString() + "," + t.K2.ToString()));
foreach (var y in q) {
    Console.WriteLine(y.Name);
}

我在LinqPad中用Linq to SQL

测试了这个

首次尝试不起作用:

我认为将其作为单个查询编写的方式就是这样的

var keys = something.Select(s => new { s.Field1, s.Field2 })
var rows = context.SomeTable.Where(r => keys.Any(k => r.Field1 == k.Field1 && r.Field2 == k.Field2));

不幸的是我在这台笔记本电脑上没有EF,甚至无法测试这是否在语法上正确。

如果它起作用的话,我也不知道它是多么高效......

答案 1 :(得分:0)

而不是.Contains(),你如何使用内连接并以这种方式“过滤”:

from s in context.SomeTable
join k in keys on new {k.Field1, k.Field2} equals new {s.Field1, s.Field2}

上面可能有拼写错误,但你明白了......

答案 2 :(得分:0)

var rows = 
from key in keys
join thingy in context.SomeTable
on 1 = 1
where thingy.Field1 == key && thingy.Field2 == key
select thingy

应该工作,并生成合理的SQL

答案 3 :(得分:0)

我遇到了完全相同的问题,我提出的解决方案是:

  • 天真:为每个本地记录执行单独查询
  • 更聪明:创建2个唯一Filed1值和唯一Fiels2值的列表,使用2包含表达式进行查询,然后您必须对结果进行双重筛选,因为它们可能不准确。

看起来像这样:

 var unique1 = something.Select(x => x.Field1).Distinct().ToList();
 var unique2 = something.Select(x => x.Field2).Distinct().ToList();
 var priceData = rows.Where(x => unique1.Contains(x.Field1) && unique2.Contains(x.Field2));
  • 下一个是我自己的解决方案,我称之为BulkSelect,其背后的想法是这样的:

    • 使用直接SQL命令
    • 创建临时表
    • 将SELECT命令的数据上传到该临时表
    • 拦截并修改由EF生成的SQL。

我为Postgres做过,但这可能需要移植到MSSQL。这个很好地描述了here,源代码是here