Как наиболее элегантно выразить левое соединение с агрегированным SQL как запрос LINQ
SQL:
SELECT
u.id,
u.name,
isnull(MAX(h.dateCol), '1900-01-01') dateColWithDefault
FROM universe u
LEFT JOIN history h
ON u.id=h.id
AND h.dateCol<GETDATE()-1
GROUP BY u.Id, u.name
Ответы
Ответ 1
Решение, хотя и отклоняющее обработку нулевого значения для кода, может быть:
DateTime вчера = DateTime.Now.Date.AddDays(-1);
var collection=
from u in db.Universe
select new
{
u.id,
u.name,
MaxDate =(DateTime?)
(
from h in db.History
where u.Id == h.Id
&& h.dateCol < yesterday
select h.dateCol
).Max()
};
Это не дает точно такой же SQL, но дает тот же логический результат. Перевод "сложных" SQL-запросов в LINQ не всегда прост.
Ответ 2
var collection=
from u in db.Universe
select new
{
u.id,
u.name,
MaxDate =(DateTime?)
(
from h in db.History
where u.Id == h.Id
&& h.dateCol < yesterday
select h.dateCol
).Max()
};
Просто вы используете приведенный выше код, и это должно работать нормально!
Ответ 3
Это не полный ответ для вас, но в левой части соединения вы можете использовать оператор DefaultIfEmpty следующим образом:
var collection =
from u in db.Universe
join history in db.History on u.id = history.id into temp
from h in temp.DefaultIfEmpty()
where h.dateCol < DateTime.Now.Date.AddDays(-1)
select u.id, u.name, h.dateCol ?? '1900-01-01'
Мне еще не нужно было делать какие-либо команды groupby, поэтому я оставил это, чтобы не отправить вас по неверному пути. Два других замечательных момента. Я не смог присоединиться к двум параметрам, хотя, как указано выше, есть способы обойти это. Так же?? оператор работает очень хорошо вместо isnull в SQL.
Ответ 4
Вам понадобится использовать конструкцию join into
для создания группового запроса.
TestContext db = new TestContext(CreateSparqlTripleStore());
var q = from a in db.Album
join t in db.Track on a.Name equals t.AlbumName into tracks
select new Album{Name = a.Name, Tracks = tracks};
foreach(var album in q){
Console.WriteLine(album.Name);
foreach (Track track in album.Tracks)
{
Console.WriteLine(track.Title);
}
}