SQL to LINQ convertion - c#

I need help producing the required C# code to implement the following SQL query. I am unsure if this is even possible?
select p.Name, r.Id, r.Status, r.Created, r.DepartmentId
from (select r.*, row_number() over (partition by personid order by created desc) as seqnum
from checkin r
where r.departmentid = '7AF20674-AEC1-4D4C-B1EA-88B1D7E8F3DB' and cast(created as date) = '2013-02-11'
) r join
person p
on r.personid = p.id
where seqnum = 1
order by p.name desc
Before i used to only have one registration per day per person, so the following was possible. Now i can have several registrations per person per day, thats why SQL has been updated.
var query = from b in context.Persons
join c in context.CheckIns
on b.Id equals c.PersonId into JoinedPersCheck
from c in JoinedPersCheck.Where(i => i.Date.Equals(date)).DefaultIfEmpty()
where b.DepartmentId.Equals(departmentId)
orderby b.Name
select new Model.PersonCheckIn
{
CheckIn = MapToCheckInModel(c),
Person = MapToModel(b),
};
return query;

Try this:
var query = context.CheckIns
.Where(p => p.DepartmentId == departmentId && p.Created.Date == date)
.GroupBy(r => r.PersonId)
.Select(g => g.OrderByDescending(p => p.Created).First())
.Join(context.Persons, c => c.PersonId, p => p.Id,
(c,p) => new { p.Name, c.Id, c.Status, c.Created, c.DepartmentId})
.OrderByDescending(f => f.Name);

Related

Linq an aggregate with a grouping

I'm translating a query and here's the original:
select top 5 t.usrID, u.FirstName, u.LastName, t.cnt as sCount
from (
select usrID, COUNT(rID) as cnt
from sessions as s where s.sDate > DATEADD(yy, -1, getdate())
group by usrID
) as t
inner join users as u on t.usrID = u.usrID
order by t.cnt desc
Here's what I have so far:
var topUser = (from p in _context.Sessions
where p.SDate > DateTime.Now.AddYears(-1)
join c in _context.Users on p.UsrId equals c.UsrId into j1
from j2 in j1.DefaultIfEmpty()
// group j2 by p.UsrId into grouped
select new
{
p.UsrId,
j2.FirstName,
j2.LastName,
cnt = p.RId
})
//.OrderBy(d => d.cnt)
//.GroupBy(o => o.UsrId)
.Take(5);
I'm having trouble figuring out how to include count() and group by clauses. When I include groupBy my other columns disappear. Thank you.
This is the answer to your answer - not to your original query. I would put it as comment, but without formatting it's hard to explain
Assuming User object has collection of Session your first statement can be drastically simplified:
var topUsers = _context.Sessions
.Where(s => s.SDate > DateTime.Now.AddYears(-1))
.Select(s => new
{
s.UsrId,
cnt = s.User.Sessions.Count(u => u.UsrId == s.UsrId)
})
.OrderByDescending(s => s.cnt)
.Take(5);
You can shape the results to get a ViewModel that also has FirstName and LastName. It all boils down to defining a model with one-to-many relationship
var topUser = (from p in _context.Sessions
where p.SDate > DateTime.Now.AddYears(-1)
join c in _context.Users on p.UsrId equals c.UsrId into j1
from j2 in j1.DefaultIfEmpty()
group j2 by p.UsrId into g
select new
{
UsrId = g.Key,
FirstName = g.Select(x => x.FirstName).FirstOrDefault(),
LastName = g.Select(x => x.LastName).FirstOrDefault(),
sCount = g.Count()
})
.OrderByDescending(d => d.sCount)
.Take(5);
Alright so it's not the most efficient but it works:
var topUsers = _context.Sessions
.Where(s => s.SDate > DateTime.Now.AddYears(-1))
.GroupBy(s => s.UsrId)
.Select(ws => new { ws.Key, cnt = ws.Count() })
.OrderByDescending(s => s.cnt)
.Take(5);
var topNamedUsers = topUsers.Join(_context.Users, ws => ws.Key, ud => ud.UsrId, (ws, ud) => new { ws.Key, ud.FirstName, ud.LastName, ws.cnt });

LINQ 3 Inner Joins with 1 Left Outer Join

Wondering why LINQ doesn't have a Left Join method. I've been trying to figure this out with myriad examples on SO, but no such luck. The other examples show simple examples with one join. If I group the joins then I only get references to the TradeCountries table in the select statement.
Being new to LINQ, I could've had this done 4 hours ago with a simple SELECT statement, but here I'm am trying to figure out why the LeftJoin method was left out of LINQ.
What does the line with "LeftJoin" need to be changed to make this work?
/*
* GetTop5Distributors
#param int array of series IDs
*/
public List<TopDistributors> Get5TopDistributors(IEnumerable<int> seriesIds)
{
_context = new MySQLDatabaseContext();
var result = _context.TradesTrades
.Join(_context.TradesSeries, tt => tt.SeriesId, ts => ts.Id, (tt, ts) => new { tt, ts })
.Join(_context.TradesTradeDistributors, tsd => tsd.tt.Id, ttd => ttd.TradeId,
(tsd, ttd) => new { tsd, ttd })
.Join(_context.TradesOrganisations, tsdto => tsdto.ttd.DistributorId, to => to.Id,
(tsdto, to) => new { tsdto, to })
.LeftJoin(_context.TradesCountries, tsdc => tsdc.to.CountryId, tc => tc.Id,
(tsdc, tc) => new {tsdc, tc})
.Where(x => seriesIds.Contains(x.tsdc.tsdto.tsd.tt.SeriesId))
.Where(x => x.tsdc.tsdto.tsd.tt.FirstPartyId == null)
.Where(x => x.tsdc.tsdto.tsd.tt.Status != "closed")
.Where(x => x.tsdc.tsdto.tsd.tt.Status != "cancelled")
.GroupBy(n => new { n.tsdc.tsdto.tsd.tt.SeriesId, n.tsdc.tsdto.ttd.DistributorId })
.Select(g =>
new TopDistributors
{
SeriesId = g.Key.SeriesId,
DistributorName = g.Select(i => i.tsdc.to.Name).Distinct().First(),
IsinNickname = g.Select(i => i.tsdc.tsdto.tsd.ts.Nickname).Distinct().First(),
CountryName = g.Select(i => i.tc.Name).Distinct().First(),
CommissionTotal = Math.Ceiling(g.Sum(i => i.tsdc.tsdto.ttd.Commission))
}
)
.OrderByDescending(x => x.CommissionTotal)
.Take(5)
.ToList();
return result;
}
Here's the rather simple select statement that is taking orders or magnitude too long to convert to LINQ.
SELECT
trades_trades.series_id,
trades_organisations.`name`,
trades_series.nickname,
trades_countries.name as Country_Name,
SUM(trades_trade_distributors.commission) as Commission_Total
FROM
trades_trades
JOIN trades_series
ON trades_series.id = trades_trades.series_id
JOIN trades_trade_distributors
ON trades_trades.id = trades_trade_distributors.trade_id
JOIN trades_organisations
ON trades_trade_distributors.distributor_id = trades_organisations.id
LEFT JOIN trades_countries
ON trades_organisations.country_id = trades_countries.id
WHERE trades_trades.series_id IN (
17,
18)
AND trades_trades.first_party_id IS NULL
AND trades_trades.status <> 'closed'
AND trades_trades.status <> 'cancelled'
GROUP BY trades_trades.series_id, trades_trade_distributors.distributor_id
ORDER BY Commission_Total DESC
Following my recipe, here is a more or less straightforward translation of the SQL to LINQ. I moved the where to be near what it constrains, and used let to create a convenient name for the Sum, as LINQ doesn't allow you to forward reference anonymous object members.
var ans = from tt in trades_trades
where new[] { 17, 18 }.Contains(tt.series_id) && tt.first_party_id == null &&
tt.status != "closed" && tt.status != "cancelled"
join ts in trades_series on tt.series_id equals ts.id
join ttd in trades_trade_distributors on tt.id equals ttd.trade_id
join to in trades_orginizations on ttd.distributor_id equals to.id
join tc in trades_countries on to.country_id equals tc.id into tcj
from tc in tcj.DefaultIfEmpty() // GroupJoin -> left join
group new { tt, ts, ttd, to, tc } by new { tt.series_id, ttd.distributor_id } into tradeg
let Commission_Total = tradeg.Sum(trade => trade.ttd.commission)
orderby Commission_Total descending
select new {
tradeg.Key.series_id,
tradeg.First().to.name,
tradeg.First().ts.nickname,
Country_Name = tradeg.First().tc == null ? null : tradeg.First().tc.name,
Commission_Total
};

Not counting null values from a linq LEFT OUTER JOIN query

I have this sql query that does exactly what i want but i need it in linq. It returns a few AVC rows and counts how many PersonAVCPermission that has status 1 linked to it
SELECT a.Id, a.Name, a.Address, COUNT(p.AVCID) AS Count
FROM AVC AS a
LEFT OUTER JOIN
(
SELECT PersonAVCPermission.AVCId
FROM PersonAVCPermission
WHERE PersonAVCPermission.Status = 1
) AS p
ON a.Id = p.AVCId
GROUP BY a.Id, a.Name, a.Address
I have this query in linq and it does the same thing except when there are no PersonAVCPermission it still counts 1
var yellows = odc.PersonAVCPermissions.Where(o => o.Status == (int)AVCStatus.Yellow);
var q = from a in odc.AVCs
from p in yellows.Where(o => o.AVCId == a.Id).DefaultIfEmpty()
group a by new { a.Id, a.Name, a.Address } into agroup
select new AVCListItem
{
Id = agroup.Key.Id,
Name = agroup.Key.Name,
Address = agroup.Key.Address,
Count = agroup.Count(o => o.Id != null)
};
Im guessing that with DefaultIfEmpty() it places null rows in the list that then gets counted so i try to exclude them with (o => o.Id != null) but it still counts everything as at least one
If i dont use DefaultIfEmpty() it skips the rows with count 0 completely
How can i exclude them or am i doing it completely wrong?
How about using .Any() and a Let?
var yellows = odc.PersonAVCPermissions.Where(o => o.Status == (int)AVCStatus.Yellow);
var q = from a in odc.AVCs
let Y = (from p in yellows.Where(o => o.AVCId == a.Id) select p).Any()
where Y == true
group a by new { a.Id, a.Name, a.Address } into agroup
select new AVCListItem
{
Id = agroup.Key.Id,
Name = agroup.Key.Name,
Address = agroup.Key.Address,
Count = agroup.Count(o => o.Id != null)
};
You don't need the join, nor the grouping:
var q = from a in odc.AVCs
select new AVCListItem
{
Id = a.Id,
Name = a.Name,
Address = a.Address,
Count = yellows.Where(o => o.AVCId == a.Id).Count()
};

Convert aggregate SQL query to LINQ

I decided to try my hand at LINQ and so far its been a miserable failure. I need to convert the following SQL query to LINQ:
SELECT
MAX(A.NEXTPAYMENTDATE) as NextPaymentDate
, SUM(B.PurchasePrice) - SUM(A.Amount) AS BALANCE
, c.FirstName
, c.LastName
, b.[year]
, b.make
, b.model
FROM Payments A
JOIN Vehicles B ON A.VehicleId = B.Id
JOIN Customers C ON b.CustomerId = c.Id
GROUP BY VehicleId, c.FirstName, c.LastName, b.[year], b.make, b.model
HAVING SUM(B.PurchasePrice) - SUM(A.Amount) > 0
This is what I have so far. It seems to work to a certain extent, but I don't know how to progress from here.
var groupedpayments =
from payments in db.Payments
group payments by new { payments.VehicleId }
into paymentGroup
let maxDate = paymentGroup.Max(x => x.NextPaymentDate)
let paid = paymentGroup.Sum(x => x.Amount)
select
new { Payments = paymentGroup.Where(x => x.NextPaymentDate == maxDate)};
I think that is what you need.
var query =
Payments.Join(Vehicles, p => p.VehicleId, v => v.Id, (p, v) => new {p, v})
.Join(Customers, d => d.v.CustomerId, c => c.Id, (d, c) => new {d, c})
.GroupBy(r =>
new {
r.d.p.VehicleId,
r.d.v.year,
r.d.v.make,
r.d.v.model,
r.c.FirstName,
r.c.LastName
},
(g, data) =>
new {
FirstName = g.FirstName,
LastName = g.LastName,
Year = g.year,
Make = g.make,
Model = g.model,
NextPaymentDate = data.Max(dd => dd.d.p.NEXTPAYMENTDATE),
Balance = data.Sum(dd => dd.d.v.PurchasePrice)
- data.Sum(dd => dd.d.p.Amount)})
.Where(r => r.Balance > 0);

Joining 2 separate LINQ queries in C# with objectFactory

I'm trying to achieve the following SQL query which works the way I need it to into LINQ. I have 2 separate SQL queries which I join and than select the product if the groups price is greater than an individual price. I'm using LINQ to objects and have an Object Factory for the Purchases and Products table.
select DISTINCT(a.Name) from
(select p.Prod_ID, p.Name, SUM(p.Price) as Total
from Tb_AvailableProduct p, Tb_Purchases o
where p.Prod_ID = o.Prod_ID
group by p.Prod_ID, p.Name) a
JOIN
(select p.Prod_ID, p.Price
from Tb_AvailableProduct p, Tb_Purchases o
where p.Prod_ID = o.Prod_ID) b
on a.Prod_ID = b.Prod_ID
where a.Total > b.Price
I have this first query in Linq, but I only want to select a Product Name if that product's group price is greater than the product's individual price.. i.e more than one product has been sold. I'm trying to accomplish this with a sum and not using a count.
from o in this.myObjectFactory.ThePurchases
join p in this.myObjectFactory.TheProducts.Values
on o.ProductID equals p.ProductID
where o.CustomerID == customer.CustomerID
group p by p.ProductID into query1
select new { ProductID = query1.Key, TotalPurchasesThisYear = query1.Sum (p => p.Price)});
Something like this should probably work (it's very similar to your SQL query):
var result =
from a in
(
from p in TheProducts
join o in ThePurchases
on p.ProductID equals o.ProductID
group p by new { p.ProductID, p.Name, p.Price } into g
select new
{
ProductID = g.Key.ProductID,
Name = g.Key.Name,
Total = g.Sum(i => i.Price)
}
)
join b in
(
from p in TheProducts
join o in ThePurchases
on p.ProductID equals o.ProductID
select new
{
ProductID = p.ProductID,
Price = p.Price
}
)
on a.ProductID equals b.ProductID
where a.Total > b.Price
select a.Name;
result = result.Distinct();

Categories

Resources