如何检查 LINQ 中设置的服务器端列表中是否缺少任何值? (SQL 给出)

How to check for the absence of any value in a list from a set server-side in LINQ? (SQL given)

给定实体 bar 的 属性 foo,实体集包含大量值,我们如何检查缺失(来自 bar) 任意值列表中的 any 值,使用 LINQ-to-Entities?

例如:


示例动态 T-SQL:

select case when not exists (
    select * from (
        select 'ALPHA' foo
        union select 'BETA' foo
        union select 'GAMMA' foo) a
    left join (select distinct foo from bar) b
    on a.foo = b.foo
    where b.foo is null)
    then 1
    else 0
end;

我们如何制定可生成此 SQL 查询(或等效查询)的 LINQ-to-Entity 查询?

注意:我以我能想到的最自然的方式编写了这个 SQL 查询(字面意思是 "check that there is no value in the list for which there is no corresponding value in the bar table")。很可能有更好的方法来做到这一点。

是这样的吗?

bool result = !context.Bars.Any(b => list.Contains(b.Foo));

编辑:我能看到的唯一选择是使用原始 SQL 查询。这将限制您可以使用的后端 DBMS,因为不同的数据库系统支持不同的语法。

您还需要对 table 和列名进行硬编码 - 如果您重构代码,它们将不会被提取。

假设微软 SQL 服务器,像这样的东西应该工作:

var list = new List<string> { "A", "B", "C" };

// Generate a parameter placeholder for each item in the list:
var listParameters = list.Select((item, index) => "@p" + index);

// Generate a "UNION" query for the parameters:
string listValues = string.Join(" UNION SELECT ", listParameters);

// The final query to execute:
string query = @"SELECT CASE WHEN NOT EXISTS
(
    SELECT 1
    FROM (SELECT " + listValues + @") As L (foo)
    WHERE NOT EXISTS
    (
        SELECT 1
        FROM bar As b
        WHERE b.foo = L.foo
    )
) THEN CAST(1 As bit) ELSE CAST(0 As bit) END";

bool result = context.Database.SqlQuery<bool>(query, list).First();

您正在寻找 All 方法

这会给你正确的答案,但它会导致对列表中的每个项目进行查询。

var list = new[] { "ALPHA", "BETA", "GAMMA" };

bool result = list.All(f => ctx.Bars.Any(b => b.Foo == f));

但是,如果将其写为子查询,它将生成单个查询。

bool result = 
    ctx.Bars.Select(x => 

              list.All(f => ctx.Bars.Any(b => b.Foo == f)) //Same as above but encapsulated 

            ).FirstOrDefault();

请注意,ctx.Bars.Select(x => 从未实际使用过,它的唯一目的是封装您的 All 逻辑。


另一种选择是在上下文中使用 SelectMany,然后仅选择列表。查询语法如下所示:

bool result = 
    (from b in ctx.Bars
     from l in list
     select l).All(f => ctx.Bars.Any(b => b.Foo == f));

所以就像上面的解决方案一样,我们只使用 from b in ctx.Bars 来强制查询成为 IQueryable 表达式而不是 IEnumerable。这将生成 1 个查询,而不是 N 个查询

我借用了 的答案,但也许你可以这样做:

var itemList = new List<string> { ... };

// select just foo
var result = context.Bars.Select(b => b.Foo)
   // get the distinct values of foo
   .Distinct()
   // count how many values of foo are in itemList and check if the counts are equal
   // because the query is distinct, the count would match iff all items were matched
   .Count(f => itemList.Contains(f)) == itemList.Count;

我链接的答案问题还包括其他可根据您的情况进行调整的选项,例如将 ExceptAny 结合使用。