【问题标题】:LINQ Query HelpLINQ 查询帮助
【发布时间】:2011-06-22 12:08:09
【问题描述】:

我有这个查询,它在 Books、TradingDesks 和 ProductInfos 上运行一个连接。每个集合中的数据都很庞大。

var queryJoin = from b in books.Values
                                    join d in tradingDesks.Values
                                        on b.TradingDeskId equals d.Id
                                    join p in ProductInfos.Values
                                        **on b.Id equals p.RiskBookId** 
                                    select new { p, Book = b.Name, TradingDeskName = d.Name };

在突出显示的行 (on b.Id equals p.RiskBookId) 中,我还想添加另一个条件,例如 (on b.Id equals p.RiskBookId || p.RiskBookId == 0) 。我如何在这个 linq 语法中做到这一点。

我试过这样查询

var queryJoin = from b in books.Values
                from d in tradingDesks.Values.Where(x => x.Id == b.TradingDeskId)
                from p in cachedProductInfos.Values.Where(y => y.RiskBookId == b.Id)
                select new { p, Book = b.Name, TradingDeskName = d.Name };

但在这种情况下,查询将永远运行,并且内存不足。所以我想以这种方式构建它会很疯狂:(

感谢任何帮助。

谢谢 玛尼

【问题讨论】:

  • 这似乎完全是 linq-to-objects,我看不出 linq-to-sql 适合的位置

标签: c# .net linq linq-to-sql linq-to-objects


【解决方案1】:

在原始查询中,对 Enumerable.Join 的调用在后台使用哈希表来加快处理速度。如果您切换到 .Where,则不会获得这些哈希好处。您可以显式使用散列来获得相同的效果。

ILookup<int, string> deskNameLookup = tradingDesks.Values
  .ToLookup(
    d => d.Id,
    d => d.Name
  );

ILookup<int, ProductInfo> infoLookup = ProductInfos.Values
  .ToLookup(p.RiskBookId);

foreach(b in books.Values)
{
  foreach(dName in deskNameLookup[b.TradingDeskId])
  {
    foreach(p in infoLookup[b.Id].Concat(infoLookup[0]))
    {
      var x = new {p, Book = b.Name, TradingDeskName = dName};
    }
  }
}

【讨论】:

  • 谢谢大卫。这看起来更有希望。但是我坚持使用此选项,因为我将此查询表达式作为 IQuerayble 并对此进行更多操作。关于如何将其加载到 IQueryable 的任何想法?
  • 将此标记为答案,尽管这不能解决问题。这解释了为什么我的查询需要比平时更长的时间。
【解决方案2】:

您可以尝试将其构建为联合而不是单个连接:

var baseQuery = 
    from book in books.Values
    join desk in tradingDesks.Values on book.TradingDeskId equals desk.Id
    select new {book, desk};

var conditionOne = 
    from baseQ in baseQuery
    join productInfo in ProductInfos.Values on baseQ.book.Id equals productInfo.RiskBookId
    select new 
    { 
        productInfo, 
        Book = baseQ.book.Name, 
        TradingDeskName = baseQ.desk.Name
    };

var conditionTwo = 
    from baseQ in baseQuery
    join productInfo in ProductInfos.Values on book.Id equals 0
    select new 
    {
        productInfo, 
        Book = baseQ.book.Name, 
        TradingDeskName = baseQ.desk.Name
    };

var result = conditionOne.Union(conditionTwo);

【讨论】:

    【解决方案3】:

    equals 只能用于单次比较。

    其他人可能会根据您的情况为您提供优化的查询方法,但添加到equals 条件并不是这里的解决方案。不过,最好的方法是以不同的方式处理它——而不是从单个查询的主体中提取所有数据——通过某种定制的加载和缓存机制,因为数据的大小显然是有问题的。

    【讨论】:

      【解决方案4】:

      试试这个,一点点逐字逐句,但应该按照你描述的那样工作:

      var queryJoin =
          from b in books.Values
          join d in tradingDesks.Values on b.TradingDeskId equals d.Id
          let p =
              from pi in ProductInfos.Values
              where (b.Id == pi.RiskBookId) || (pi.RiskBookId == 0)
              select pi
          where p.Any()
          select new
          {
              p,
              Book = b.Name,
              TradingDeskName = d.Name
          };
      

      【讨论】:

      • 问题可能是(pi.RiskBookId == 0),如果您有大量符合该条件的记录,则取决于您的数据
      • 你是对的。从另一个角度考虑,我所需要的只是 ProductInfo 集合的外部连接。仍在研究同一个,还没有解决方案....
      【解决方案5】:

      其实:

      join d in tradingDesks.Values
          on (b.Id equals p.RiskBookId || p.RiskBookId equals 0)
      

      on (b.Id == p.RiskBookId || p.RiskBookId == 0)
      

      应该可以正常工作。

      连接需要一个真实的条件:

      on <expression>
      
      (b.Id equals p.RiskBookId || p.RiskBookId == 0) 
      

      将返回真或假,因此连接应该这样评估。

      【讨论】:

      • 这不能编译!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! !!!!!var queryJoin = from b in books.Values join d in tradingDesks.Values on b.TradingDeskId 等于 d.Id join p in cachedProductInfos.Values on (b.Id 等于 p.RiskBookId || p.RiskBookId 等于 0 ) 选择新的 { p, Book = b.Name, TradingDeskName = d.Name };
      • 嗯,Linq 与 SQL 非常相似。 :)