LINQ Left Join с группировкой и преобразованием этого TSQL - PullRequest
1 голос
/ 12 ноября 2010

Следующий TSQL предоставляет пример того, как я мог бы решить эту проблему с SQL. Цель состоит в том, чтобы вернуть 1 строку на OID из левой таблицы, где количество записей в левой таблице равно количеству совпадающих строк в правой таблице.

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. В приведенном ниже примере настройки результат должен быть нулевым. Добавление еще одной дороги с правильными значениями приведет к возврату только одной. По крайней мере, это то, что идеально.

Проблема, с которой я столкнулся, заключается в том, что этот тип кода 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 ]

2 голосов
/ 12 ноября 2010
  • Ваше предложение 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}

Цель состоит в том, чтобы вернуть 1 строку на OID из левой таблицы,где Количество записей в левой таблице равно количеству совпадающих строк в правой таблице.

Основываясь на этом описании, я написал бы:

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 голосов
/ 12 ноября 2010

Я не уверен, что полностью понимаю, каковы желаемые результаты, но вот мой шанс на это. Поле 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
              };
...