9

在 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. 对于每个键,查询表(如果您有相当数量的行要拉入,这会变慢)

有时,我能够做出的妥协是修改 #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'd 在一起的 .Where() 子句。有人有魔法子弹吗?

4

4 回答 4

1

代替 .Contains(),您如何使用内部联接并以这种方式“过滤”:

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

上面可能有错别字,但你明白了......

于 2013-12-04T15:57:41.620 回答
1

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

  • 天真:对每个本地记录进行单独的查询
  • 更智能:创建 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。这很好地描述了这里,源代码在这里

于 2017-11-01T15:08:30.427 回答
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,甚至无法测试这在语法上是否正确。

如果它完全有效,我也不知道它的性能如何......

于 2013-09-07T21:54:44.970 回答
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

于 2013-12-04T16:33:15.600 回答