C# 使用LINQ动态连接实体
假设你有一个关键字表。为了简单起见,让我们假设它有两个字段,一个Id整数,关键字varchar100。一个查询包含多个关键字。例如,查询quick brown fox。我们的要求是,如果所选Id包含至少一次所有三个关键字,我们将获取任何记录。此外,它可以是使用StartsWith的部分匹配。我可以使用PredicateBuilder来构建最终需要的多个OR子句,但为了过滤这些记录,我需要为每个关键字在同一个表上执行联接。我应该注意,Id字段不是唯一的,可以有多个条目 SQL看起来或多或少像这样,或者应该是这样C# 使用LINQ动态连接实体,c#,sql-server,linq,C#,Sql Server,Linq,假设你有一个关键字表。为了简单起见,让我们假设它有两个字段,一个Id整数,关键字varchar100。一个查询包含多个关键字。例如,查询quick brown fox。我们的要求是,如果所选Id包含至少一次所有三个关键字,我们将获取任何记录。此外,它可以是使用StartsWith的部分匹配。我可以使用PredicateBuilder来构建最终需要的多个OR子句,但为了过滤这些记录,我需要为每个关键字在同一个表上执行联接。我应该注意,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();
为了使用这个结果,我必须先做一个比较,然后再返回我的结果。这可能会产生巨大的内存需求,我正试图找到一种解决方案,在服务器上实现我所需的大部分功能。要重新表述查询,您需要查找ID值,以便该ID的所有关键字组包含您要查找的所有搜索词 您可以使用GroupBy,而不是尝试使用Join为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);
最终,我能够让它生成我所寻找的SQL。尽管这个问题很可能是我独有的,我还是会发布我的解决方案。也许它会对某些人有用 林克
当然,在开始循环之前,我会进行一些检查,以确保我有超过1个searchTerm。但是你明白了。除非所有的单词都是一样的,否则你的第一次查询怎么会产生任何结果?或者列关键字是否包含多个单词?@ErikPhilips存在具有相同ID的不同关键字。ID不是主键。通过执行联接更正@Servy,我确保返回的所有记录中至少有一个传入的关键字。这就消除了只有一个或两个关键字匹配的Id。我喜欢简短的查询,但是结果SQL会导致索引扫描超过168M行,这就不太理想了。@ewahner好吧,到目前为止,您还没有一个有效的查询。效率低下但能提供正确答案的查询肯定比不能提供正确答案的查询要好。你开始使用的版本除了是静态的而不是动态的外,还将对组中的所有关键字执行笛卡尔乘积,以查看它们中的哪些恰好与你的搜索词匹配。我有一个SQL查询可以运行,但我希望将它转换为LINQ,因为它通过CLR函数动态地构建所有连接和where谓词。变得很难维护。@ewahner您打开的连接查询只有三个关键字,而不是未知数量的关键字。如果您确实总是有一个静态的关键字数,那么将查询精确映射到LINQ是非常简单的;这两个查询看起来几乎完全相同。它不是静态的……这只是一个例子。可以传入任意数量的关键字。它最多只限于200个字符。
var query = keywords.GroupBy(keyword => keyword.Id)
.Where(group => searchTerms.All(term =>
group.Any(keyword => keyword.Keyword.StartsWith(term)))
.Select(group => group.Key);
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;
}
-- 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 '~')