【问题标题】:Performance linq group by with count性能 linq group by with count
【发布时间】:2013-01-18 09:02:41
【问题描述】:

我有一个使用 linq 和 group by 表达式的方法:

public List<SerieVu> ListSeriesLesPlusVuesSemaine()
{
    DateTime dateIlYaSeptJours = DateTime.Now.AddDays(-7);

    var resultat =
    from uev in Query(uev => uev.userepivu_date > dateIlYaSeptJours)
    group uev by uev.Episode.Saison.Serie into pgroup
    let count = pgroup.Count()
    orderby count descending
    select new SerieVu() { nombreDeVus = count, Serie = pgroup.Key };

    return resultat.Take(7).ToList();
}

这个 linq 查询生成这个 SQL 查询:

SELECT TOP (7) [Project1].[C2]                        AS [C1],
               [Project1].[C1]                        AS [C2],
               [Project1].[serie_id]                  AS [serie_id],
               [Project1].[serie_image]               AS [serie_image],
               [Project1].[serie_image_thumb]         AS [serie_image_thumb],
               [Project1].[serie_format]              AS [serie_format],
               [Project1].[serie_motsclefs]           AS [serie_motsclefs],
               [Project1].[serie_statusproduction_id] AS [serie_statusproduction_id],
               [Project1].[serie_nom]                 AS [serie_nom],
               [Project1].[serie_nomvf]               AS [serie_nomvf],
               [Project1].[serie_synopsis]            AS [serie_synopsis],
               [Project1].[serie_syncthetvdb]         AS [serie_syncthetvdb],
               [Project1].[serie_dateajout]           AS [serie_dateajout],
               [Project1].[serie_datemiseajour]       AS [serie_datemiseajour],
               [Project1].[serie_actif]               AS [serie_actif],
               [Project1].[utilisateur_id]            AS [utilisateur_id],
               [Project1].[serie_francais]            AS [serie_francais]
FROM   (SELECT [GroupBy1].[A1]  AS [C1],
               [GroupBy1].[K1]  AS [serie_id],
               [GroupBy1].[K2]  AS [serie_image],
               [GroupBy1].[K3]  AS [serie_image_thumb],
               [GroupBy1].[K4]  AS [serie_format],
               [GroupBy1].[K5]  AS [serie_motsclefs],
               [GroupBy1].[K6]  AS [serie_statusproduction_id],
               [GroupBy1].[K7]  AS [serie_nom],
               [GroupBy1].[K8]  AS [serie_nomvf],
               [GroupBy1].[K9]  AS [serie_synopsis],
               [GroupBy1].[K10] AS [serie_syncthetvdb],
               [GroupBy1].[K11] AS [serie_dateajout],
               [GroupBy1].[K12] AS [serie_datemiseajour],
               [GroupBy1].[K13] AS [serie_actif],
               [GroupBy1].[K14] AS [utilisateur_id],
               [GroupBy1].[K15] AS [serie_francais],
               1                AS [C2]
        FROM   (SELECT [Extent4].[serie_id]                  AS [K1],
                       [Extent4].[serie_image]               AS [K2],
                       [Extent4].[serie_image_thumb]         AS [K3],
                       [Extent4].[serie_format]              AS [K4],
                       [Extent4].[serie_motsclefs]           AS [K5],
                       [Extent4].[serie_statusproduction_id] AS [K6],
                       [Extent4].[serie_nom]                 AS [K7],
                       [Extent4].[serie_nomvf]               AS [K8],
                       [Extent4].[serie_synopsis]            AS [K9],
                       [Extent4].[serie_syncthetvdb]         AS [K10],
                       [Extent4].[serie_dateajout]           AS [K11],
                       [Extent4].[serie_datemiseajour]       AS [K12],
                       [Extent4].[serie_actif]               AS [K13],
                       [Extent4].[utilisateur_id]            AS [K14],
                       [Extent4].[serie_francais]            AS [K15],
                       COUNT(1)                              AS [A1]
                FROM   [dbo].[UtilisateurEpisodeVu] AS [Extent1]
                       INNER JOIN [dbo].[Episode] AS [Extent2]
                         ON [Extent1].[episode_id] = [Extent2].[episode_id]
                       INNER JOIN [dbo].[Saison] AS [Extent3]
                         ON [Extent2].[saison_id] = [Extent3].[saison_id]
                       LEFT OUTER JOIN [dbo].[Serie] AS [Extent4]
                         ON [Extent3].[serie_id] = [Extent4].[serie_id]
                WHERE  [Extent1].[userepivu_date] > '2013-01-11T09:53:26' /* @p__linq__0 */
                GROUP  BY [Extent4].[serie_id],
                          [Extent4].[serie_image],
                          [Extent4].[serie_image_thumb],
                          [Extent4].[serie_format],
                          [Extent4].[serie_motsclefs],
                          [Extent4].[serie_statusproduction_id],
                          [Extent4].[serie_nom],
                          [Extent4].[serie_nomvf],
                          [Extent4].[serie_synopsis],
                          [Extent4].[serie_syncthetvdb],
                          [Extent4].[serie_dateajout],
                          [Extent4].[serie_datemiseajour],
                          [Extent4].[serie_actif],
                          [Extent4].[utilisateur_id],
                          [Extent4].[serie_francais]) AS [GroupBy1]) AS [Project1]
ORDER  BY [Project1].[C1] DESC

我的问题是如何提高这个查询的性能?在我网站的一个非常重要的页面上大约需要 6 秒。

感谢您的帮助

【问题讨论】:

    标签: performance linq entity-framework group-by


    【解决方案1】:

    您可以从在数据库中创建一些索引开始 您应该将字段表单 where、order by 和 group by 放在索引中 您可能会通过在 userepivu_date 上创建索引来获得最大的性能改进,因为这个 WHERE [Extent1].[userepivu_date] > '2013-01-11T09:53:26' 将扫描你的整个桌子

    在 SQL Server 上创建索引:

    CREATE INDEX [indexname] ON Extent1 (userepivu_date)
    

    【讨论】:

    • 感谢您的回答。不幸的是,我已经有相关表的索引
    【解决方案2】:

    我应该给出的第一个建议是查看执行计划并寻找瓶颈

    那么,为什么要使用 LEFT OUTER JOIN 呢?

    LEFT OUTER JOIN [dbo].[Serie] AS [Extent4]

    不应该是内部连接吗(如果是这样,您的映射可能有问题)? 我想它可能会通过阻止在连接过程中早期执行 where 子句来搞砸查询优化。

    也许您可以尝试运行查询,将左外连接替换为内连接,看看是否有区别。 另外,同意@user1802430 ,如果userepivu_date上没有索引,你肯定需要放一个。

    【讨论】:

    • 非常感谢您的回答。我尝试使用 INNER JOIN 而不是 LEFT OUTER JOIN,性能非常好:300 毫秒。现在我的问题是:如何用 linq 做到这一点?因为这个 SQL Query 是由 Linq 生成的
    • @toregua 您必须手动编写连接代码。显然Saison.Serie 不是必填字段,因此 EF 创建了一个外连接。
    • @toregua 不熟悉 EF:您可能必须更改模型或代码映射,以便在 Saison 和 Serie 之间拥有一个外键,使其成为 1,* 关系而不是 0,*。或添加一个必需的属性,如 GertArnold Bon 勇气所建议的那样
    • Saison 和 Serie 之间已经有 1, * 关系。Saison 和 Serie 之间的外键也存在 :(
    【解决方案3】:

    我找到了解决方案。我已经使用显式连接强制内部连接:

    public List<SerieVu> ListSeriesLesPlusVuesSemaine()
    {
        DateTime dateIlYaSeptJours = DateTime.Now.AddDays(-7);
    
        ObjectSet<UtilisateurEpisodeVu> UtilisateurEpisodeVuSet = _context.CreateObjectSet<UtilisateurEpisodeVu>();
        ObjectSet<Episode> EpisodeSet = _context.CreateObjectSet<Episode>();
        ObjectSet<Saison> SaisonSet = _context.CreateObjectSet<Saison>();
        ObjectSet<Serie> SerieSet = _context.CreateObjectSet<Serie>();
    
        var resultat =
        from uev in UtilisateurEpisodeVuSet.Include("Episode.Saison.Serie").Where(uev => uev.userepivu_date > dateIlYaSeptJours)
        join e in EpisodeSet on uev.episode_id equals e.episode_id
        join sai in SaisonSet on e.saison_id equals sai.saison_id
        join s in SerieSet on sai.serie_id equals s.serie_id
        group uev by s into pgroup
        let count = pgroup.Count()
        orderby count descending
        select new SerieVu() { nombreDeVus = count, Serie = pgroup.Key };
    
        return resultat.Take(7).ToList();
    }
    

    使用这种方法,linq 查询只生成内部连接;)感谢所有试图帮助我的人

    【讨论】:

    • 感谢跟进。如果可以,您应该接受自己的答案。还想知道,如果您有相反的关系,您是否可以反过来开始:从系列中的 se 从 se.saisons 中的 sa ... where uev.userepivu_date > dateIlYaSeptJours group by uev by se into pgroup ...
    猜你喜欢
    • 1970-01-01
    • 2020-08-02
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-05-20
    • 1970-01-01
    • 2020-10-30
    相关资源
    最近更新 更多