【问题标题】:Left Join LINQ and using Bitwise ComparisonsLeft Join LINQ 并使用按位比较
【发布时间】:2010-11-15 11:27:25
【问题描述】:

左连接 LINQ 并使用按位比较。

我有一个问题可以描述为(感谢 David B 澄清这一点): 目标是从左表中为每个 OID 返回 1 行,其中左表中的记录计数等于右表中匹配行的计数。在 FLAGS 中为一行设置 OID、RID 和 FLAG 时,记录匹配。

我们正在比较的对象具有以下结构:

public class Roads : List<Road>{}
public class Road
{
    public int RID;
    public int OID;
    public int Check = 1;
    public long Flag;
}
public class Cars : List<Car> { }
public class Car
{
    public int RID;
    public int OID;
    public long Flags;
}

用以下数据填充的对象。

        Roads rs = new Roads();
        Cars cs = new Cars();

        Car c = new Car();
        c.OID = 1;
        c.RID = 1;
        c.Flags = 31; // 11111
        cs.Add(c);
        c = new Car();
        c.OID = 1;
        c.RID = 2;
        c.Flags = 31; //11111
        cs.Add(c);
        c = new Car();
        c.OID = 1;
        c.RID = 3;
        c.Flags = 4; //00100
        cs.Add(c);

        Road r = new Road();
        r.OID = 1;
        r.RID = 1;
        r.Flag = 8; //00010
        rs.Add(r);
        r = new Road();
        r.OID = 1;
        r.RID = 2;
        r.Flag = 2; //01000
        rs.Add(r);
        r = new Road();
        r.OID = 1;
        r.RID = 3;
        r.Flag = 4;  //01000
        rs.Add(r);
      //  r = new Road();
      //  r.OID = 1;
      //  r.RID = 3;
      //  r.Flag = 16;  //00001
      //  rs.Add(r);

要查看是否设置了标志,请进行按位比较,即 cs[0].Flags && rs[0].Flag > 0 为 TRUE,cs[0].Flags & rs[0].Flag = 0是假的

我有这个通用查询,它将获取 cs 中的 OID 计数 = rs 中匹配 OID 的计数的行。我现在需要一个修改后的查询,其中应用了其余规则。我们检查Flag是否在特定行匹配的Flag中。

    var carLookup = cs.ToLookup(cb => c.OID);
    var roadLookup = rs.ToLookup(rb => r.OID);

    var results1 = from x in carLookup
                   let carCount = x.Count()
                   let roadCount = roadLookup[x.Key].Count()
                   where carCount == roadCount
                   select new {  OID = x.Key, CarCount = carCount, RoadCount = roadCount };

如何扩展它以应用其他过滤条件?我正在苦苦挣扎的是在我需要它们来构建适当的过滤条件的地方提供可用的列。比如我需要比较Flags && Flag。但是我如何才能访问 Flag 和 Flags 来做额外的过滤呢?

展开。我主要使用 TSQL,所以我试图模仿一个可以轻松应用于 TSQL 的逻辑流程。如果我使用 TSQL 执行此操作,它将如下所示(注意 0 的特殊情况):

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  
 AND (CS.FLAGS & RS.FLAG > 0
      OR (CS.FLAGS=0 AND RS.FLAG=0))
GROUP BY cs.OID  
HAVING Count(cs.OID) = Sum(RS.Check)   

使用该语句和上面的数据,结果将是 1、3、3。

如果我要注释最后添加到 Roads,并取消注释下一行,将 Flag 更改为 16,那么结果将是: 无效的 如果您需要更多信息,请发表评论。

【问题讨论】:

    标签: linq linq-to-objects


    【解决方案1】:

    我认为这应该等同于您的 SQL 语句,但是它没有产生您提到的结果,因为您的 SQL 使用 RS.Check 但您没有在示例代码中分配任何 Check 值(您使用 @ 987654323@)。我继续将r.Check = 1; 添加到每个Road 对象中,并且能够得到您提到的结果。

    var query = from car in cs
                from road in rs
                where car.OID == road.OID
                    && car.RID == road.RID
                    && ((car.Flags & road.Flag) > 0 || (car.Flags == 0 && road.Flag == 0))
                group new { car, road } by car.OID into grouping
                let CarCount = grouping.Count()
                let RoadCount = grouping.Sum(o => o.road.Check)
                where CarCount == RoadCount
                select new { OID = grouping.Key, CarCount, RoadCount };
    

    【讨论】:

    • 感谢您的反馈.... 对 TSQL 感到抱歉。您的解决方案的唯一问题是它总是返回相同的 CarCount 和 RoadCount 计数,因为它正在执行完全匹配。所以对于上面的示例数据,结果是 OID = 1,CarCount = 3,RoadCount = 3。更改最后添加的道路,使其标志为 16 而不是 4。结果是 OID=1,CarCount=2,RoadCount= 2.对于这个问题的真正解决方案,最后一个标志为 16,OID = 1,CarCount = 3 和 RoadCount =2。 (我在示例类中为 Check 添加了默认值)
    • 虽然 Ahmad 的解决方案不是 100% 存在,但他能够让我对 Linq 有更多的了解,并了解到最终确实没有仅使用单个 Linq 语句的可靠解决方案。即使只使用 Linq,性能也明显低于解决此问题的其他方法。
    猜你喜欢
    • 1970-01-01
    • 2015-07-14
    • 2017-01-31
    • 1970-01-01
    • 1970-01-01
    • 2023-01-02
    • 2018-07-10
    • 2011-08-03
    • 1970-01-01
    相关资源
    最近更新 更多