2010-11-12 41 views
1

以下TSQL提供的我会如何解决这个问题,SQL的例子。目标是从左表中返回每个OID 1行,其中左表中的记录计数等于右表中匹配行的计数。LINQ左连接与分组并具有转换这个TSQL

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查询,或者这是不可能的?注意道路类声明中检查的默认值。在下面的设置示例中,结果应该是零匹配。添加一个具有适当值的道路将使它返回一个。至少这是理想的。

我所遇到的问题是,它似乎这种类型的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 
} 

回答

2
  • 你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} 
+0

尼斯使用的查找。我总是忽视那种方法。 – Sorax 2010-11-12 20:25:46

+0

我会试一试并提供反馈。此示例是更复杂的查询的简化版本,但具有特定的问题点。注意cs.OID在汽车中是独一无二的,所以不需要区分。这个想法是我在左表中有3行,并且我想只在右表中匹配3行时才返回一行。因此,对于非匹配,SQL(rs.check)中的Count(CS.OID)和SUM(rs.check)将为空,并且SQL忽略总和中的空值。 – MikeH 2010-11-12 21:20:43

+0

如果您加入... cs.OID在结果中不会是唯一的。而且由于你忽略了空值,所以你不需要使用LEFT JOIN来为你忽略空值。 – 2010-11-12 21:30:47

0

我不知道我完全理解所期望的结果是什么,但这里是我的投篮吧。对于LINQ来说,check字段并不是真正需要的,因为除了计数之外,它没有任何其他用途。

我误解的加入,什么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 
       }; 
+0

感谢您的尝试。用你的例子,我结束了两行。每个包含。 OID = 1,CarCount = 1,RoadCount = 1。根据上面示例中的数据,结果应该为空,因为有3辆车和2条道路。但是如果我们不依赖HAVING,如果我们只返回左连接,它将是一个OID = 1,CarCount = 3,Roadcount = 2的单行。如果我能做到这一点,这将是一个积极的举措前锋。 – MikeH 2010-11-13 21:52:29

+0

@Mike:对,它会产生两行,因为我们通过匹配'OID'和'RID'来加入。由于结果只包含'OID',因为我们忽略了'RID',所以它们看起来完全相同。但这是基于我对您的TSQL代码的解释。现在我对想要的结果有了更好的了解,看起来我们应该将它们分组并加入到“OID”中。 – 2010-11-14 00:44:12

+0

我们不能只在OID上进行分组。我们需要对RID和OID进行分组,因为这是匹配的。值得注意的是,我们需要找到RID + OID的分组,其中有与汽车中所有行匹配的分组。例如。如果我正在建造一个装配。该组件将有3个组件。要知道构建的程序集是否正确,我们必须将构建与规则进行比较。一种方式来做到这一点,SQL显示这一点)是左侧将构建加入到规则中。如果规则由3个组件构成,但构建只由2个组件构成,则构建失败。 (在这种情况下,结果为空) – MikeH 2010-11-14 16:55:25