使用LINQ to Entities动态JOIN

时间:2014-07-24 16:32:32

标签: c# sql-server linq

所以说你有一张关键词表。为简单起见,我们可以说它有2个字段,一个Id整数,Keyword varchar(100)。查询包含多个关键字。例如,查询“快速棕色狐狸”。要求是我们将采取所选Id包含至少一次出现所有三个关键字的任何记录。而且,它可以是使用StartsWith的部分匹配。我可以使用PredicateBuilder来构建最终需要的多个OR子句,但也要过滤这些记录,我需要在同一个表上为每个关键字执行JOIN。我应该注意,Id字段不是唯一的,可以有多个条目。

SQL看起来或多或少都是这样或应该

select k1.Id
  from Keywords k1
 inner join Keywords k2 on k1.Id = k2.Id
 inner join Keywords k3 on k2.Id = k3.Id
 where k1.Keyword like @k1
   and k2.Keyword like @k2
   and k3.Keyword like @k3

我到目前为止的LINQ将是

var predicate = PredicateBuilder.False<Keyword>();
foreach (string term in searchTerms)
{
   string temp = term;
   predicate = predicate.Or(p => p.Keyword.StartsWith(temp));
}
var keys = Keywords.AsExpandable().Where(predicate).ToList();

这将产生或多或少的SQL:

SELECT 
[Extent1].[Id] AS [Id]
FROM  [dbo].[Keywords] AS [Extent1]
WHERE ([Extent1].[Keyword] LIKE @p__linq__1 ESCAPE '~') OR ([Extent1].[Keyword] LIKE @p__linq__2 ESCAPE '~') OR ([Extent1].[Keyword] LIKE @p__linq__3 ESCAPE '~')

为了使用这个结果,我必须做一个DistinctBy然后JOIN回到我的结果。这可能会产生巨大的内存需求,我正在尝试找到一种能够在服务器上完成大部分工作的解决方案。

2 个答案:

答案 0 :(得分:0)

要重新定义查询,您需要查找ID值,以便该ID的所有关键字组包含您要查找的所有搜索字词。

您可以使用Join,而不是尝试使用GroupBy创建ID的所有关键字组,然后当您拥有所有关键字的组共享ID时,它很简单足以过滤出该组包含所有搜索词的那些。

var query = keywords.GroupBy(keyword => keyword.Id)
    .Where(group => searchTerms.All(term => 
        group.Any(keyword => keyword.Keyword.StartsWith(term)))
    .Select(group => group.Key);

答案 1 :(得分:0)

最终我能够让它生成我想要的SQL。即使这个问题对我来说是独一无二的,我也会发布我的解决方案。也许对某人有用。

LINQ

var term = searchTerms[0];
var keys = Keywords.Where (k => k.keyword.StartsWith(term));
for(var i=1; i < searchTerms.Length; i++)
{
    var searchTerm = searchTerms[i];
    keys = 
        from k1 in keys
        join k2 in Keywords on k1.Id equals k2.Id
        where k2.keyword.StartsWith(searchTerm)
        select k1;
}

PRODUCED SQL:

-- Region Parameters
DECLARE @p__linq__0 VarChar(1000) = 'the%'
DECLARE @p__linq__1 VarChar(1000) = 'brown%'
DECLARE @p__linq__2 VarChar(1000) = 'fox%'
-- EndRegion
SELECT 
    [Extent1].[Id] AS [Id]
    FROM    [dbo].[Keywords] AS [Extent1]
    INNER JOIN [dbo].[Keywords] AS [Extent2] ON [Extent1].[Id] = [Extent2].[Id]
    INNER JOIN [dbo].[Keywords] AS [Extent3] ON [Extent1].[Id] = [Extent3].[Id]
    WHERE ([Extent1].[keyword] LIKE @p__linq__0 ESCAPE '~') AND ([Extent2].[keyword] LIKE @p__linq__1 ESCAPE '~') AND ([Extent3].[keyword] LIKE @p__linq__2 ESCAPE '~')

当然我会做一些检查以确保在开始循环之前我有超过1个searchTerm。但你明白了。