c# - LINQ/EF 为性能声明临时变量

标签 c# sql linq entity-framework

我有一个数据库查询。我可以使用原始 SQL 快速完成,但我真的很想在 LINQ 中做类似的事情

declare @ruleInstanceCount int
select @ruleInstanceCount
    = (select count(distinct(gri.RuleInstance_Id))
       from [Group] g  
       left join GroupRuleInstance gri on g.id = gri.Group_Id
       where g.name= @groupName)

SELECT cache.EntityKey
from RuleInstanceCurrentMembershipCacheItem cache
left join RuleInstance ri on ri.id = cache.RuleInstanceId 
left join GroupRuleInstance gri on ri.id = gri.RuleInstance_Id
left join [Group] g on g.id = gri.Group_Id 
where cache.IsMember = 1 and g.Name = @groupName 
group by cache.EntityKey
HAVING  count(cache.RuleInstanceId) = @ruleInstanceCount
order by cache.EntityKey

Yay SQL 真的很快,因为我可以声明一个变量并在以后使用它

这是迄今为止我对 LINQ 的最佳尝试,但速度慢了大约 50 倍

from g in ctx.Groups
where g.Name == groupName
let ruleCount = g.Rules.Select(r => r.Id).Distinct().Count()
from ri in g.Rules
from mem in ri
    .CurrentMembership
    .Where(m => m.IsMember)
    .Select(m => new { m.EntityKey, m.RuleInstanceId, ruleCount })
group mem by mem.EntityKeyinto ekGroup
let ruleCount = ekGroup.Any() ? ekGroup.FirstOrDefault().ruleCount : 0
where ekGroup.Count() == ruleCount
orderby ekGroup.Key
select ekGroup.Key

生成的 SQL 有点乱(如果需要我可以发布)但是这和我手写的 SQL 有两个重要的区别。

  1. 没有 declare 语句 - 这意味着它一遍又一遍地执行实际的 @ruleInstanceCount。
  2. 比较看到规则实例的次数时没有 having 子句。

我认为它很慢是因为 1 但我不知道有什么方法可以进行声明或类似操作,因此它只执行一次计数。

enter image description here

编辑:

这里请求的是生成的SQL

SELECT 
    [Project13].[EntityKey] AS [EntityKey]
    FROM ( SELECT 
        [Project12].[EntityKey] AS [EntityKey]
        FROM ( SELECT 
            [Project11].[EntityKey] AS [EntityKey], 
            [Project11].[C1] AS [C1], 
            [Project11].[C2] AS [C2], 
            [Project11].[C3] AS [C3], 
            (SELECT 
                COUNT(1) AS [A1]
                FROM   [dbo].[Group] AS [Extent17]
                INNER JOIN [dbo].[GroupRuleInstance] AS [Extent18] ON [Extent17].[Id] = [Extent18].[Group_Id]
                INNER JOIN [dbo].[RuleInstanceCurrentMembershipCacheItem] AS [Extent19] ON ([Extent18].[RuleInstance_Id] = [Extent19].[RuleInstanceId]) AND ([Extent19].[IsMember] = 1)
                WHERE ((([Extent17].[Name] = @p__linq__0) AND ( NOT ([Extent17].[Name] IS NULL OR @p__linq__0 IS NULL))) OR (([Extent17].[Name] IS NULL) AND (@p__linq__0 IS NULL))) AND ([Project11].[EntityKey] = [Extent19].[EntityKey])) AS [C4]
            FROM ( SELECT 
                [Project10].[EntityKey] AS [EntityKey], 
                [Project10].[C1] AS [C1], 
                [Project10].[C2] AS [C2], 
                (SELECT 
                    COUNT(1) AS [A1]
                    FROM   [dbo].[Group] AS [Extent14]
                    INNER JOIN [dbo].[GroupRuleInstance] AS [Extent15] ON [Extent14].[Id] = [Extent15].[Group_Id]
                    INNER JOIN [dbo].[RuleInstanceCurrentMembershipCacheItem] AS [Extent16] ON ([Extent15].[RuleInstance_Id] = [Extent16].[RuleInstanceId]) AND ([Extent16].[IsMember] = 1)
                    WHERE ((([Extent14].[Name] = @p__linq__0) AND ( NOT ([Extent14].[Name] IS NULL OR @p__linq__0 IS NULL))) OR (([Extent14].[Name] IS NULL) AND (@p__linq__0 IS NULL))) AND ([Project10].[EntityKey] = [Extent16].[EntityKey])) AS [C3]
                FROM ( SELECT 
                    [Project9].[EntityKey] AS [EntityKey], 
                    [Project9].[C1] AS [C1], 
                    (SELECT 
                        COUNT(1) AS [A1]
                        FROM   [dbo].[Group] AS [Extent11]
                        INNER JOIN [dbo].[GroupRuleInstance] AS [Extent12] ON [Extent11].[Id] = [Extent12].[Group_Id]
                        INNER JOIN [dbo].[RuleInstanceCurrentMembershipCacheItem] AS [Extent13] ON ([Extent12].[RuleInstance_Id] = [Extent13].[RuleInstanceId]) AND ([Extent13].[IsMember] = 1)
                        WHERE ((([Extent11].[Name] = @p__linq__0) AND ( NOT ([Extent11].[Name] IS NULL OR @p__linq__0 IS NULL))) OR (([Extent11].[Name] IS NULL) AND (@p__linq__0 IS NULL))) AND ([Project9].[EntityKey] = [Extent13].[EntityKey])) AS [C2]
                    FROM ( SELECT 
                        [Project7].[EntityKey] AS [EntityKey], 
                        CASE WHEN ( EXISTS (SELECT 
                            1 AS [C1]
                            FROM   [dbo].[Group] AS [Extent8]
                            INNER JOIN [dbo].[GroupRuleInstance] AS [Extent9] ON [Extent8].[Id] = [Extent9].[Group_Id]
                            INNER JOIN [dbo].[RuleInstanceCurrentMembershipCacheItem] AS [Extent10] ON ([Extent9].[RuleInstance_Id] = [Extent10].[RuleInstanceId]) AND ([Extent10].[IsMember] = 1)
                            WHERE ((([Extent8].[Name] = @p__linq__0) AND ( NOT ([Extent8].[Name] IS NULL OR @p__linq__0 IS NULL))) OR (([Extent8].[Name] IS NULL) AND (@p__linq__0 IS NULL))) AND ([Project7].[EntityKey] = [Extent10].[EntityKey])
                        )) THEN [Project7].[C1] ELSE 0 END AS [C1]
                        FROM ( SELECT 
                            [Project6].[EntityKey] AS [EntityKey], 
                            [Project6].[C1] AS [C1]
                            FROM ( SELECT 
                                [Project2].[EntityKey] AS [EntityKey], 
                                (SELECT TOP (1) 
                                    [Project4].[C1] AS [C1]
                                    FROM    (SELECT 
                                        [Extent4].[Id] AS [Id], 
                                        (SELECT 
                                            COUNT(1) AS [A1]
                                            FROM ( SELECT DISTINCT 
                                                [Extent5].[RuleInstance_Id] AS [RuleInstance_Id]
                                                FROM [dbo].[GroupRuleInstance] AS [Extent5]
                                                WHERE [Extent4].[Id] = [Extent5].[Group_Id]
                                            )  AS [Distinct2]) AS [C1]
                                        FROM [dbo].[Group] AS [Extent4]
                                        WHERE (([Extent4].[Name] = @p__linq__0) AND ( NOT ([Extent4].[Name] IS NULL OR @p__linq__0 IS NULL))) OR (([Extent4].[Name] IS NULL) AND (@p__linq__0 IS NULL)) ) AS [Project4]
                                    INNER JOIN [dbo].[GroupRuleInstance] AS [Extent6] ON [Project4].[Id] = [Extent6].[Group_Id]
                                    INNER JOIN [dbo].[RuleInstanceCurrentMembershipCacheItem] AS [Extent7] ON ([Extent6].[RuleInstance_Id] = [Extent7].[RuleInstanceId]) AND ([Extent7].[IsMember] = 1)
                                    WHERE [Project2].[EntityKey] = [Extent7].[EntityKey]) AS [C1]
                                FROM ( SELECT 
                                    [Distinct1].[EntityKey] AS [EntityKey]
                                    FROM ( SELECT DISTINCT 
                                        [Extent3].[EntityKey] AS [EntityKey]
                                        FROM   [dbo].[Group] AS [Extent1]
                                        INNER JOIN [dbo].[GroupRuleInstance] AS [Extent2] ON [Extent1].[Id] = [Extent2].[Group_Id]
                                        INNER JOIN [dbo].[RuleInstanceCurrentMembershipCacheItem] AS [Extent3] ON ([Extent2].[RuleInstance_Id] = [Extent3].[RuleInstanceId]) AND ([Extent3].[IsMember] = 1)
                                        WHERE (([Extent1].[Name] = @p__linq__0) AND ( NOT ([Extent1].[Name] IS NULL OR @p__linq__0 IS NULL))) OR (([Extent1].[Name] IS NULL) AND (@p__linq__0 IS NULL))
                                    )  AS [Distinct1]
                                )  AS [Project2]
                            )  AS [Project6]
                        )  AS [Project7]
                    )  AS [Project9]
                )  AS [Project10]
            )  AS [Project11]
        )  AS [Project12]
        WHERE (([Project12].[C2] = [Project12].[C1]) AND ( NOT ([Project12].[C3] IS NULL OR [Project12].[C1] IS NULL))) OR (([Project12].[C4] IS NULL) AND ([Project12].[C1] IS NULL))
    )  AS [Project13]
    ORDER BY [Project13].[EntityKey] ASC

和模型:

public class Group
{
    public int Id { get; set; }
    public string Name { get; set; }
    public ICollection<RuleInstance> Rules { get; set; }
}

public class RuleInstance
{
    public Guid Id { get; set; }
    public ICollection<Group> Groups { get; set; }
    public ICollection<RuleInstanceCurrentMembershipCacheItem> CurrentMembership { get; set; }
}

public class RuleInstanceCurrentMembershipCacheItem
{
    public Guid RuleInstanceId { get; set; }
    public int EntityKey { get; set; }
    public RuleInstance RuleInstance { get; set; }
    public bool IsMember { get; set; }
}

编辑:

根据@MarcinJuraszek 的回答,我做出以下决定:

var ruleInstances = from g in ctx.Groups
            from ri in g.Rules
            where g.Name == groupName
            select ri.Id;

var cache = (   from g in ctx.Groups
        where g.Name == groupName
        from ri in g.Rules
        from mem in ri.CurrentMembership
        where mem.IsMember
        group mem by mem.AnimalKey into akGroup
        where akGroup.Count() == ruleInstances.Distinct().Count()
        orderby akGroup.Key
        select akGroup.Key).ToArray();

它产生以下查询

SELECT 
[Project4].[EntityKey] AS [EntityKey]
FROM ( SELECT 
    [GroupBy1].[K1] AS [EntityKey]
    FROM     (SELECT 
        [Extent3].[EntityKey] AS [K1], 
        COUNT(1) AS [A1], 
        COUNT(1) AS [A2], 
        COUNT(1) AS [A3]
        FROM   [dbo].[Group] AS [Extent1]
        INNER JOIN [dbo].[GroupRuleInstance] AS [Extent2] ON [Extent1].[Id] = [Extent2].[Group_Id]
        INNER JOIN [dbo].[RuleInstanceCurrentMembershipCacheItem] AS [Extent3] ON ([Extent2].[RuleInstance_Id] = [Extent3].[RuleInstanceId]) AND ([Extent3].[IsMember] = 1)
        WHERE (([Extent1].[Name] = @p__linq__0) AND ( NOT ([Extent1].[Name] IS NULL OR @p__linq__0 IS NULL))) OR (([Extent1].[Name] IS NULL) AND (@p__linq__0 IS NULL))
        GROUP BY [Extent3].[EntityKey] ) AS [GroupBy1]
    CROSS JOIN  (SELECT 
        COUNT(1) AS [A1]
        FROM ( SELECT DISTINCT 
            [Extent5].[RuleInstance_Id] AS [RuleInstance_Id]
            FROM  [dbo].[Group] AS [Extent4]
            INNER JOIN [dbo].[GroupRuleInstance] AS [Extent5] ON [Extent4].[Id] = [Extent5].[Group_Id]
            WHERE (([Extent4].[Name] = @p__linq__1) AND ( NOT ([Extent4].[Name] IS NULL OR @p__linq__1 IS NULL))) OR (([Extent4].[Name] IS NULL) AND (@p__linq__1 IS NULL))
        )  AS [Distinct1] ) AS [GroupBy2]
    CROSS JOIN  (SELECT 
        COUNT(1) AS [A1]
        FROM ( SELECT DISTINCT 
            [Extent7].[RuleInstance_Id] AS [RuleInstance_Id]
            FROM  [dbo].[Group] AS [Extent6]
            INNER JOIN [dbo].[GroupRuleInstance] AS [Extent7] ON [Extent6].[Id] = [Extent7].[Group_Id]
            WHERE (([Extent6].[Name] = @p__linq__1) AND ( NOT ([Extent6].[Name] IS NULL OR @p__linq__1 IS NULL))) OR (([Extent6].[Name] IS NULL) AND (@p__linq__1 IS NULL))
        )  AS [Distinct2] ) AS [GroupBy3]
    CROSS JOIN  (SELECT 
        COUNT(1) AS [A1]
        FROM ( SELECT DISTINCT 
            [Extent9].[RuleInstance_Id] AS [RuleInstance_Id]
            FROM  [dbo].[Group] AS [Extent8]
            INNER JOIN [dbo].[GroupRuleInstance] AS [Extent9] ON [Extent8].[Id] = [Extent9].[Group_Id]
            WHERE (([Extent8].[Name] = @p__linq__1) AND ( NOT ([Extent8].[Name] IS NULL OR @p__linq__1 IS NULL))) OR (([Extent8].[Name] IS NULL) AND (@p__linq__1 IS NULL))
        )  AS [Distinct3] ) AS [GroupBy4]
    WHERE (([GroupBy1].[A1] = [GroupBy2].[A1]) AND ( NOT ([GroupBy1].[A2] IS NULL OR [GroupBy3].[A1] IS NULL))) OR (([GroupBy1].[A3] IS NULL) AND ([GroupBy4].[A1] IS NULL))
)  AS [Project4]
ORDER BY [Project4].[EntityKey] ASC

最佳答案

我会尝试以下操作:

var ctx = new Context();

var groupName = "something";

var ruleInstances = from g in ctx.Groups
                    from gri in g.Rules.DefaultIfEmpty()
                    where g.Name == groupName
                    select gri.Id;

var keys = from c in ctx.CurrentMembership
           let ri = c.RuleInstance
           from g in ri.Groups.DefaultIfEmpty()
           where c.IsMember && g.Name == groupName
           group c by c.EntityKey into g
           where g.Select(x => x.RuleInstanceId).Count() == ruleInstances.Distinct().Count()
           orderby g.Key
           select g.Key;

它应该给你的 SQL 非常接近于:

SELECT cache.EntityKey
from RuleInstanceCurrentMembershipCacheItem cache
left join RuleInstance ri on ri.id = cache.RuleInstanceId 
left join GroupRuleInstance gri on ri.id = gri.RuleInstance_Id
left join [Group] g on g.id = gri.Group_Id 
where cache.IsMember = 1 and g.Name = @groupName 
group by cache.EntityKey
HAVING  count(cache.RuleInstanceId) =
       (select count(distinct(gri.RuleInstance_Id))
       from [Group] g  
       left join GroupRuleInstance gri on g.id = gri.Group_Id
       where g.name= @groupName)
order by cache.EntityKey

我假设你的上下文看起来很接近那个:

public class Context
{
    public IQueryable<Group> Groups { get; set; }
    public IQueryable<RuleInstance> Rules { get; set; }

    public IQueryable<RuleInstanceCurrentMembershipCacheItem> CurrentMembership { get; set; }
}

关于c# - LINQ/EF 为性能声明临时变量,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/22005669/

相关文章:

c# - 动态 linq 嵌套组

c# - 什么决定了 NavigationCommands.BrowseBack 是否调用页面构造函数?

sql - Netezza SQL 查询中的字符串运算符

linq - 为什么谓词在通过反射构建时不过滤

c# - linq2entities 的 IQueryable 扩展方法

sql - 为什么 GORM 使用连接表进行一对多?

c# - 我们可以在运行应用程序时编辑我们的代码吗

c# - 使用正则表达式获取html标签的变量值

c# - Xamarin Android - 调用 Environment.GetExternalStorageState(文件路径)时出现异常

sql - 带计数的数据透视表 - Oracle SQL