I'm trying to rewrite following SQL query into LINQ:
SELECT `i`.`symbol`, `i`.`id`, `t0`.`close`, `t`.`close`, `t`.`close` - `t0`.`close`, (`t`.`close` - `t0`.`close`) / `t0`.`close`
FROM `investment` AS `i`
LEFT JOIN `investment_record` AS `t0` ON `t0`.id = (
SELECT `i0`.id
FROM `investment_record` AS `i0`
WHERE (`i0`.`date` <= @dateFrom) AND i.id = i0.investment_id
ORDER BY `i0`.`date` DESC
LIMIT 1
)
LEFT JOIN `investment_record` AS `t` ON `t`.id =(
SELECT `i0`.id
FROM `investment_record` AS `i0`
WHERE (`i0`.`date` <= @dateTo) AND i.id = i0.investment_id
ORDER BY `i0`.`date` DESC
LIMIT 1
)
WHERE `i`.`id` IN (@id0, @id1, ....)
My main issues are the AND i.id = i0.investment_id
and LIMIT 1
parts of JOINs.
Currently the best I could achieve is this:
from inv in _context.Investment
join recTo in _context.InvestmentRecord on inv.Id equals recTo.InvestmentId into recToColl
from recToNullable in recToColl.Where(x => x.Date <= dateTo).OrderByDescending(x => x.Date).Take(1).DefaultIfEmpty()
join recFrom in _context.InvestmentRecord on inv.Id equals recFrom.InvestmentId into recFromColl
from recFromNullable in recFromColl.Where(x => x.Date <= dateFrom).OrderByDescending(x => x.Date).Take(1).DefaultIfEmpty()
where investmentIds.Contains(inv.Id)
let amountFrom = recFromNullable.Close
let amountTo = recToNullable.Close
select new InvestmentPerformance(
inv.Symbol,
inv.Id,
amountFrom,
amountTo,
amountTo - amountFrom,
(amountTo - amountFrom) / amountFrom
);
but the problem is it doesn't work.
It gives the expression cannot be translated exception:
System.InvalidOperationException: The LINQ expression 'DbSet() .GroupJoin( inner: DbSet(), outerKeySelector: inv => inv.Id, innerKeySelector: recTo => recTo.InvestmentId, resultSelector: (inv, recToColl) => new { inv = inv, recToColl = recToColl })' could not be translated. Either rewrite the query in a form that can be translated, or switch to client evaluation explicitly by inserting a call to 'A sEnumerable', 'AsAsyncEnumerable', 'ToList', or 'ToListAsync'. See https://go.microsoft.com/fwlink/?linkid=2101038 for more information.
Point of this ugly SQL (and LINQ) is to calculate performance of investment for given time interval. User is able to specify from-to dates. Problem is sometimes user could specify date without any records (bank holiday for example). So for given date, I want to use the closest previous record (that is the reason for <= @dateFrom
conditions and ORDER BY date DESC LIMIT 1
parts of the SQL.
I tried many variations of the LINQ with different forms of joins, but none of them worked as I need :(
I'm using EF.Core 5 and MySQL database.