【问题标题】:LINQ Left Join with Grouping and Having Convert this TSQLLINQ Left Join with Grouping and have Convert this TSQL
【发布时间】:2010-11-15 03:12:21
【问题描述】:

以下 TSQL 提供了一个示例,说明如何使用 SQL 解决此问题。目标是从左表中为每个 OID 返回 1 行,其中左表中的记录计数等于右表中匹配行的计数。

SELECT cs.OID, Count(cs.OID) AS CarCount, Sum(RS.Check) AS RoadCount  
   FROM Cars AS cs  
LEFT JOIN Roads AS RS  
  ON CS.oid = RS.OID  
   AND cs.RID = RS.RID  
  GROUP BY cs.OID  
  HAVING Count(cs.OID) = Sum(RS.Check)  

使用下面的对象设置,是否可以构造等效的 LINQ 查询,或者这不可能?请注意在 Road 类的声明中为 check 提供的默认值。在下面的设置示例中,结果应该是零匹配。再添加一条具有适当值的道路将使其仅返回一条。至少那是最理想的。

我遇到的问题是这种类型的 TSQL 代码对于 LINQ 来说似乎太复杂了。我还没有找到一个明显或不明显的解决方案来实现类似的行为。因此,我相信也许解决方案是停止尝试复制 SQL 并做一些不同的事情。由于没有足够的 LINQ 经验,我不知道从哪里开始。

public class Roads : List<Road>{}  
public class Road  
{  
    public int RID;  
    public int OID;  
    public int check = 1;  
}  
public class Cars : List<Car> { }  
public class Car  
{  
    public int RID;  
    public int OID;  
}  

private void CheckCheck()  
{  
    Roads rs = new Roads();  
    Cars cs = new Cars();  

    Car c = new Car();  
    c.OID = 1;  
    c.RID = 1;  
    cs.Add(c);  
    c = new Car();  
    c.OID = 1;  
    c.RID = 2;  
    cs.Add(c);  
    c = new Car();  
    c.OID = 1;  
    c.RID = 3;  
    cs.Add(c);  

    Road r = new Road();  
    r.OID = 1;  
    r.RID = 1;  
    rs.Add(r);  
    r = new Road();  
    r.OID = 1;  
    r.RID = 2;  
    rs.Add(r);  

    // Results should be :  
    // OID where Count of OID from C = Count of OID from R  
}  

【问题讨论】:

    标签: linq linq-to-objects


    【解决方案1】:
    • 您的 HAVING 子句将过滤掉任何与道路不匹配的汽车。这使得左连接变成了内连接。
    • 你有COUNT(cs.OID),它说它正在计算汽车,但它没有。你的意思可能是COUNT(DISTINCT cs.OID)

    这里是直译:

    from c in Cars
    join r in Roads on new {c.OID, c.RID} equals new {r.OID, r.RID}
    group new {Car = c, Road = r} by c.OID into g
    let carCount = g.Count()  //did you mean g.Select(x => x.Car.OID).Distinct().Count()
    let roadCount = g.Sum(x => x.Road.Check)
    where carCount = roadCount
    select new {OID = g.Key, CarCount = carCount, RoadCount = roadCount}
    

    目标是从左表中为每个 OID 返回 1 行,其中左表中的记录计数等于右表中匹配行的计数。

    根据这个描述,我会写:

    var carLookup = Cars.ToLookup(c => c.OID);
    var roadLookup = Roads.ToLookup(r => r.OID);
    
    from x in carLookup
    let carCount = x.Count()
    let roadCount = roadLookup[x.Key].Count()
    where carCount = roadCount
    select new {OID = g.Key, CarCount = carCount, RoadCount = roadCount}
    

    【讨论】:

    • 很好地使用了查找。我总是忽略这种方法。
    • 我会试一试并提供反馈。此示例是更复杂查询的简化版本,但有特定的问题点。注意 cs.OID 在 Cars 中是唯一的,所以不需要 distinct。我的想法是我在左表中有 3 行,并且只有当我在右表中也有匹配的 3 行时才想返回一行。因此 Count(CS.OID) 和 SUM(rs.check) - 在 SQL 中,rs.check 对于不匹配将为空,并且 SQL 忽略总和中的空值。
    • 如果你加入... cs.OID 在结果中不会是唯一的。而且由于您忽略了空值,因此您不需要 LEFT JOIN 来生成空值以供您忽略。
    • 嗯,对于第一个示例,我收到错误“WindowsFormsApplication1.Road”不包含“检查”的定义,并且没有扩展方法“检查”接受类型为“WindowsFormsApplication1.Road”的第一个参数' 可以找到(您是否缺少 using 指令或程序集引用?在 Check in this 一词下方带有蓝色方格:行:让 roadCount = g.Sum(x => x.Road.C​​heck)。我会做明天再玩。谢谢大家。
    • 接近了,我会用你的例子来玩,也许我自己会找到一个解决方案。上面的例子有问题。 #1 - 使用原始问题中设置的对象,这将返回 OID = 1、CarCount =2、RoadCount = 2 的单行。是的,它们都有 2,但 CarCount 应该是 3。因此左连接。 #2 - 产生一条消息 至少一个对象必须实现 IComparable。左连接似乎是这里的症结所在。
    【解决方案2】:

    我不确定我是否完全理解所需的结果,但这是我的尝试。 check 字段对于 LINQ 来说并不是必需的,因为它除了用于计数之外没有任何用途。

    我误解了连接的重要性以及HAVING 子句对查询的作用。它现在又回到了原来的 TSQL 版本,但 RoadCount 计算发生了变化。我相信这就是您所要求的。

    var results = from Car in cs
                  join road in rs
                      on new { Car.OID, Car.RID } equals new { road.OID, road.RID }
                      into Roads
                  group roads by Car.OID into cars
                  let CarCount = cars.Count()
                  let RoadCount = cars.Sum(roads => roads.Count())
                  where CarCount == RoadCount
                  select new
                  {
                      OID = cars.Key,
                      CarCount,
                      RoadCount
                  };
    

    【讨论】:

    • 感谢您的尝试。以您的示例为例,我最终得到两行。各含。 OID = 1,CarCount = 1,RoadCount = 1。使用上面示例中的数据,结果应该是空的,因为有 3 辆汽车和 2 条道路。但是如果我们不依赖 HAVING,如果我们只返回左连接,它将是 OID = 1、CarCount = 3、Roadcount = 2 的单行。如果我能做到这一点,那将是一个积极的举措前进。
    • @Mike:是的,它会产生两行,因为我们通过匹配OIDRID 加入。由于结果仅包括OID,因此它们看起来相同,因为我们省略了RID。但这是基于我对您的 TSQL 代码的解释。现在我对想要的结果有了更好的了解,看来我们应该只在OID 上进行分组和加入。
    • 我们不能只在 OID 上分组。我们需要对 RID 和 OID 进行分组,因为这是匹配的。问题是我们需要找到与汽车中所有行匹配的 RID + OID 分组。例如。如果我正在构建一个程序集。该组件将包含 3 个组件。要知道构建的程序集是否正确,我们必须将构建与规则进行比较。这样做的一种方法,SQL 显示了这一点)是将构建左连接到规则中。如果规则由 3 个组件组成,但构建仅由 2 个组件组成,则构建失败。 (在这种情况下为空结果)
    • 我应该注意到我以前的版本相当于大卫的查找版本(结果是你最终使用的版本)。 RIDOID 既没有加入也没有分组,所以我对你想要完成的事情感到茫然。虽然现在,我的最新版本使它等同于他的直译。
    • 看起来不错。谢谢你。我也有点不知所措。这实际上只是第一部分。我还需要做更多的事情。这样做的目的是在帖子中复制 TSQL 的行为。我在这里将这个问题扩展为更多细节:stackoverflow.com/questions/4179550/…
    猜你喜欢
    • 1970-01-01
    • 2010-12-13
    • 2015-12-10
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多