【问题标题】:How to optimize this linq query?如何优化这个 linq 查询?
【发布时间】:2011-04-13 08:18:51
【问题描述】:

我有以下 linq 查询:

var allnews = from a in db.News
                              where !(from c in db.NewsViews
                                      where c.UserGuid == thisUser.UserGuid
                                      select c.NewsGuid).Contains(a.NewsGuid)
                              orderby a.Date descending
                              select a;

我想知道优化它的最佳方法是什么?或者查询分析器会为我做这件事吗?

编辑:这个想法是获取用户尚未看到的所有新闻项目。因此,一旦用户看到了一个项目,我就将该项目存储在 NewsViews 中。新闻本身就在新闻中。

【问题讨论】:

  • 其实不慢,但是现在表里几乎没有数据。尽管这不是最好的方法,但必须有一个更快的“更好”解决方案。
  • 所以以后再担心速度......当它很重要时

标签: c# .net sql linq optimization


【解决方案1】:

您可以添加到内部查询的 where 语句中,而不是使用包含:

... 和 c.newsguid == a.newsguid

和你的内部查询的 .Any()

var allnews = from a in db.News
                  where !(from c in db.NewsViews
                          where c.UserGuid == thisUser.UserGuid
                            and c.NewsGuid == a.NewsGuid).Any()
                   orderby a.Date descending
                   select a;

【讨论】:

  • 是的,它返回集合/查询中是否有任何项目。在子查询中包含相等条件的结果完全相同,只是子查询中的集合更小,并且没有使用'in'语句。
  • 对,所以:new[]{false,false}.Any() 为真,因为集合不为空 - 布尔值都不成立这一事实无关紧要。
  • 我不确定您指的是哪一点,但需要明确的是,您当前的表述等同于 OP - 它不会返回任何新闻项目对于查看过任何新闻的用户来说完全没有。
  • 对不起,我明白你的意思了。我的错,选择应该是,就像我在解释中所说的那样。只是复制错误。
  • 从语法上讲,您需要执行类似... where !(from c in db.NewsViews where c.UserGuid == thisUser.UserGuid && c.NewsGuid == a.NewsGuid select 0).Any()
【解决方案2】:

子查询好像没有使用a,所以

      //untested
      var allnews = from a in db.News
                    let excluders = from c in db.NewsViews
                                    where c.UserGuid == thisUser.UserGuid
                                    select c.NewsGuid   
                          where !excluders.Contains(a.NewsGuid)
                          orderby a.Date descending
                          select a;

但请注意,您现在正在通过 LINQ 进行 SQL 优化(顺便说一句,这是 L2S 还是 EF?)。
而普通的 SQL 优化已经够难了。您必须使用真实数据进行测量和分析。 @Joachim 的多内连接子查询方法很可能更好。

【讨论】:

  • +1 指出估计这将如何执行非常棘手 - 测试一下!
  • 顺便说一句,将子查询放入 let 子句不会改变原始查询的语义,所以如果这会更好地改变性能,那就令人惊讶了——尽管你永远不知道...
  • @Earnon:我假设有一些缓存,但你是对的,这对于 IEnumerable 来说并不明显。但这是一个 IQueryable,我指望 SQL 服务器。
  • 是的,LINQ 会产生什么 SQL,内部执行计划 SQL 会产生什么,这并不明显——测试确实是最好的判断方式。
【解决方案3】:

我假设目标是按日期降序检索 NewsViews:

db.News.OrderByDescending(a => a.Date).NewsViews;

当然,这假设您已经在模型中建立了 News 和 NewsViews 实体之间的关联。通过提前设置关联,子查询就变得不必要了。

更新:

我已经使用 LINQ-to-SQL 大约 18 个月了,我一直在使用与您为我的 NOT IN 查询说明的构造相同的构造。正如我之前所说,如果您提前在模型中设置关联并在数据库本身中使用索引,您可能会获得一点性能提升,但从 LINQ 的角度来看,我相信您和您一样优化'无需求助于不必要的神秘查询语句即可获得。

【讨论】:

    【解决方案4】:

    也许这是我缺乏 linq 知识,但也许是左连接,其中 NewsViews 中的列为空?这似乎比创建子查询并比较两者要好。

    【讨论】:

      【解决方案5】:

      这是另一种表述:

      from newsitem in db.News
      join viewing in (
             from viewing in db.NewsViews
             where viewing.UserGuid == thisUser.UserGuid
             select viewing
      ) on newsitem.NewsGuid equals viewing.NewsGuid into usersviewings
      where !usersviewings.Any()
      orderby newsitem.Date descending
      select newsitem;
      

      但至于这是否更快 - 这是任何人的猜测;试试吧。从根本上说,你正在做一个左连接,左部分被过滤并且不能返回任何结果——这不能很好地索引,AFAIK。执行引擎需要扫描新闻集中的所有行,如果你有 SQL 支持,那么表扫描不是你的朋友。话虽如此,除非您真的期望这是一个巨大的表,否则它可能并不重要,特别是如果您只报告前 N 次点击...

      【讨论】:

        【解决方案6】:

        您可以在此处进行的最佳优化举措是允许从 NewsViews 导航到 News...由于不存在,我不得不对优化。

        db.News.Join(db.News.Select(n => n.NewsGuid)
            .Except(db.NewsViews
                .Where(c => c.UserGuid == thisUser.UserGuid)
                .Select(c => c.NewsGuid)
            ), n1 => n1.NewsGuid, n2 => n2, (n1, n2) => new { n1 = n1, n2 = n2 })
            .Select(anon => anon.n1);
        
        当您尝试执行一个列表不包含另一个列表的查询时,

        一个例外将产生 最佳 执行 SQL。由于没有从 NewsView 到 News 的导航,我们必须使用 Inner Join 作弊才能返回 News。

        另一种方法是我的朋友 GroupJoin。

        db.News
            .GroupJoin(db.NewsViews, n => n.NewsGuid, nv => nv.NewsGuid, (n, nv) => new { News = n, NewsViewList = nv })
            .Where(anon => anon.NewsViewList != null) // I don't remember the best test here, either it's not null, or the count > 0 :-)
            .OrderByDescending(anon => anon.News.Date)
            .Select(anon => anon.News);
        

        至少我会这样做。

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 2012-12-22
          • 1970-01-01
          • 2013-03-01
          相关资源
          最近更新 更多