问题
Left Join LINQ and using Bitwise Comparisons.
I have a problem that can be described as (thanks to David B for clarifying this): The goal is to return 1 row per OID from the Left Table, where the Count of the records in the left table is equal to the Count of the matching rows in the right table. A record matches when the OID, RID, and the FLAG is set in FLAGS for a row.
The Objects we are comparing have the following structure:
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;
}
The objects a filled with the following data.
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);
To see if a flag is set you do a bitwise comparison, i.e. cs[0].Flags && rs[0].Flag > 0 is TRUE, cs[0].Flags & rs[0].Flag = 0 is FALSE
I have this general query that will get me the rows where the count of OID in cs = the count of matching OID in rs. I need a modified query now where the rest of the rules are applied. Where we check if the Flag is in the Flag for the specific row match.
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 };
How can I extend this to get the additional filter conditions applied? What I am struggling with is having columns available where I need them to build the proper filter conditions. For example, I need to compare Flags && Flag. But how do I get so I have access to Flag and Flags to do the additional filter?
To Expand. I work mostly with TSQL, so I'm trying to mimic a logic flow I can easily apply in TSQL. If I was doing this with TSQL, it would look like this (note special case for 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)
With that statement, and the data above, the result would be 1, 3, 3.
If I were to comment the last add to Roads, and uncomment the next line, changing the Flag to 16, then the result would be: NULL Please comment if you need more info.
回答1:
I think this should be equivalent to your SQL statement, however it didn't produce the result you mentioned since your SQL uses RS.Check
but you didn't assign any Check
values in your sample code (you used r.OID
). I went ahead and added r.Check = 1;
to each Road
object and was able to get the result you mentioned.
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 };
来源:https://stackoverflow.com/questions/4179550/left-join-linq-and-using-bitwise-comparisons