I have an SQL query that I built for a tool a while ago and I'm remaking the tool in MVC and using LINQ to Entities.
I can't seem to figure out how to sort my list of Brands by weighting my Cars by man hours and their testing value.
Here's the SQL query I had in the old tool:
SELECT Brand.ID, SUM(Car.EstManHours) - SUM(Car.EstManHours) * CAST(AVG(1.00 * TestingStatus.Value) AS DECIMAL(9 , 2)) / 100 AS Weighting
FROM TestingStatus INNER JOIN Car ON TestingStatus.ID = Car.StatusID
INNER JOIN Team ON Car.TeamID = Team.TeamID
RIGHT OUTER JOIN Brand
LEFT OUTER JOIN SubCategory ON Brand.ID = SubCategory.BrandID ON Car.SubCategoryID = SubCategory.ID
WHERE (Car.IsPunted == 'False')
GROUP BY Brand.YearID, Brand.FeatID
HAVING (Brand.YearID = #BrandYearID)
ORDER BY Weighting DESC
I've tried this, but whether I put descending or ascending the order doesn't actually change in the list, it keeps the sorting by Id:
var brands = (from b in _context.Brands
join s in _context.SubCategorys on f.Id equals s.BrandId
join c in _context.Cars on s.Id equals c.SubCategoryId
where (f.YearId == yearId && c.IsPunted == false)
orderby (c.ManHoursEst - (c.ManHoursEst * c.TestingStatu.Value / 100)) descending
select b).Distinct().ToList();
Would appreciate help on this conversion!
Thanks.
EDIT:
I'm now trying to get the order by and group by to work correctly.
The following query is listing tons of duplicates and not ordering properly as I don't think my weighting is done correctly.
var brands = (from b in _context.Brands
join s in _context.SubCategorys on f.Id equals s.BrandId
join c in _context.Cars on s.Id equals c.SubCategoryId
where (f.YearId == yearId && c.IsPunted == false)
let weighting = c.ManHoursEst - (c.ManHoursEst * c.TestingStatu.Value / 100)
orderby weighting descending
group b by b.Id).SelectMany(x=>x).ToList();
Any ideas?
Distinct does not preserve sorting. That is your problem.
You could do a group by like in your SQL to mimic the Distinct and perform everything server side.
Related
I want to convert this SQL code to LINQ. Here is my SQL code:
SELECT Rooms.RoomName AS RoomNo, Beds.BedName AS Beds, Rooms.RoomType, ISNULL(CheckIn.CheckIntatus,'') AS Status
FROM CheckIn
INNER JOIN GuestBeds ON CheckIn.GuestBedId = GuestBeds.Id
AND (CheckIn.CheckInStatus = 1 OR CheckIn.CheckIntatus = 2 OR CheckIn.CheckSIntatus = 3)
RIGHT JOIN Beds ON GuestBeds.BedId = Beds.Id
INNER JOIN Rooms ON Beds.RoomId = Rooms.Id
LEFT JOIN Guests ON CheckIn.GuestId = Guests.Id
WHERE Beds.Active = 1 AND Rooms.Active = 1
ORDER BY RoomName, Beds
It works well which means it shows all the RoomName with CheckInStatus. If the Room is not presence in CheckIn table, ot will return the status as Null.
So I want to convert the code to LINQ. SO here is my LINQ code:
from b in Beds
join w in Rooms on b.RoomsId equals w.Id
where (a.CheckInStatus == 3 || a.CheckInStatus == 1 || a.CheckInStatus == 2)
join p in GuestBeds on b.Id equals p.BedId
join a in CheckIn on p.Id equals a.GuestBedId
join t in Guests on a.GuestId equals t.Id
where b.Active == true && w.Active == true
orderby w.RoomName
select new
{
RoomName = w.RoomName,
BedName = b.BedName,
Status = a.CheckInStatus
}
It didnt worked like the first code. It only show the data which contain CheckInStatus. I want it to show all the RoomName inside Room database
Normally I would post some rules for converting SQL to LINQ but this is complicated enough I think I'd need to make new rules. I commented out the references to Guests because as a LEFT JOIN it has no bearing on the answer.
Pull out the WHERE on individual tables and make them sub-queries:
var ActiveBeds = Beds.Where(b => b.Active == 1);
var ActiveRooms = Rooms.Where(r => r.Active == 1);
In LINQ, a RIGHT JOIN must be done by flipping the join to be a left join, so we will create the two sides as sub-queries.
Left side of RIGHT JOIN:
Translate the JOIN conditions that aren't part of an equi-join into a LINQ where clause on the appropriate tables (alternately this could be a subquery as above). The LEFT JOIN becomes a LINQ join/from ... DefaultIfEmpty() phrase, but as noted above isn't needed.
var CheckInsGuestBedsGuests = from c in CheckIn
where (c.CheckInStatus == 1 || c.CheckInStatus == 2 || c.CheckInStatus == 3)
join gb in GuestBeds on c.GuestBedId equals gb.Id
//join g in Guests on c.GuestId equals g.Id into gj
//from g in gj.DefaultIfEmpty()
select new { c, gb /*, g */ };
Right side of RIGHT JOIN:
The other side of the RIGHT JOIN includes an INNER JOIN so put them together in a sub-query:
var ActiveBedsRooms = from b in ActiveBeds
join r in ActiveRooms on b.RoomId equals r.Id
select new { b, r };
Finally, flip the sub-queries to create a left join using the same idiom as above:
var ans = from br in ActiveBedsRooms
join cgbg in CheckInsGuestBedsGuests on br.b.Id equals cgbg.gb.BedId into cgbgj
from cgbg in cgbgj.DefaultIfEmpty()
select new {
RoomNo = br.r.RoomName,
Beds = br.b.BedName,
br.r.RoomType,
Status = cgbg.c.CheckInStatus
};
NOTE: If you were not using LINQ to SQL, the Status expression would fail when cgbg is null and you would need
Status = cgbg?.c.CheckInStatus
but unfortunately LINQ to SQL/EF doesn't handle the null conditional operators yet.
BTW, nice query - brings back memories of when I used to write hotel front desk software :)
I need to retrieve a list of orders records, and on the same view I have to show for each order:
how many invoices have been issued (InvoiceCount)
the total amount covered by the invoices (Invoiced)
how much has been paid for the issued invoices (Payed)
all on the same grid view.
I think it's a basic view for business management software.
Order > invoices = one-to-many
Payments are related to invoices with a look-up table PaymentsToInvoices
GridView is paged server-side, with a page-size of 50 records.
I've written the following LINQ, that it works fine, but I have some doubt that is well written in terms of performance,
especially if this query is finally translated with a couple of sub-queries, that means 50*2 queries for each request!
var orders = (from o in db.Orders
join c in db.Customers on o.CustomerID equals c.CustomerID
join os in db.OrderStatuses on o.OrderStatusID equals os.OrderStatusID
join ps in db.PaymentStatuses on o.PaymentStatusID equals ps.PaymentStatusID
join ec in db.EntryChannels on o.EntryChannelID equals ec.EntryChannelID
orderby o.InsertDate descending
where o.OrderStatusID == 2
select new
{
o.OrderID,
o.CustomerID,
o.InsertDate,
o.TotalPrice,
o.Notes,
c.FirstName,
c.LastName,
OrderStatus = os.Name,
PaymentStatus = ps.Name,
EntryChannel = ec.Name,
InvoiceCount = (db.InvoiceDetails.Where(i => i.OrderID == o.OrderID).Select(s => s.InvoiceID).Distinct().Count()),
Invoiced = (db.InvoiceDetails.Where(i => i.OrderID == o.OrderID).Select(s => s.TotalPrice).Sum()),
Payed = (from py in db.Payments
join pti in db.PaymentsToInvoices on py.PaymentID equals pti.PaymentID
join inv in db.InvoiceDetails on pti.InvoiceID equals inv.InvoiceID
where inv.OrderID==o.OrderID
select new { py.PaymentID, py.Amount }).Distinct().Select(s=>s.Amount).Sum()
});
What do you think about the last three fields? Can they be improved in some way? Do they result in performance killer sub-queries?
I'm sorry for telling that I've a little bit weak on LINQ, I always do write SQL query before I start working on the complicated LINQ.
I want to ask that how to convert this SQL Query into LINQ with LEFT JOIN with multiple ON conditons with the OR operator.,
m.MerchandiseId will be use for twice in ON condition
SELECT
*
FROM
Inbox AS i
INNER JOIN [User] AS u ON i.FromUserId = u.UserId
LEFT OUTER JOIN Merchandise AS m ON
u.MerchandiseId = m.MerchandiseId
OR
i.ToMerchantId = m.MerchandiseId
WHERE
i.ToCompanyId = 10
OR
i.FromCompanyId = 10
var message = (from i in db.Inbox
join u in db.User on i.FromUserId equals u.UserId
join m in db.Merchandise on u.MerchandiseId equals m.MerchandiseId //here I want to ON i.MerchantId = m.MerchandiseId, but it doesn't allow
where i.ToCompanyId == user.CompanyId || i.FromCompanyId == user.CompanyId
orderby i.CreatedAt descending
group m.MerchandiseId by new { m.MerchandiseId, m.MerchandiseName } into grp
select new
{
MerchandiseId = grp.Key.MerchandiseId,
MerchandiseName = grp.Key.MerchandiseName,
InboxMessage = (from e in db.Inbox
join us in db.User on e.FromUserId equals us.UserId
join mer in db.Merchandise on us.MerchandiseId equals mer.MerchandiseId
where mer.MerchandiseId == grp.Key.MerchandiseId
orderby e.CreatedAt descending
select e.InboxMessage).FirstOrDefault(),
CreatedAt = (from e in db.Inbox
join us in db.User on e.FromUserId equals us.UserId
join mer in db.Merchandise on us.MerchandiseId equals mer.MerchandiseId
where mer.MerchandiseId == grp.Key.MerchandiseId
orderby e.CreatedAt descending
select e.CreatedAt).FirstOrDefault(),
}).ToList();
The bottom LINQ Query I've write for it. However, I just can work on the left join with multiple ON clause in LINQ. Appreciate if someone would help me on this. Thanks!
I don't believe Linq supports the use of the OR operator with multiple columns, but that said, I wouldn't use OR even in SQL as it makes the join's intention unclear and it also obscures where the data originated from - it also isn't immediately clear what happens if there are multiple matches for each column. Instead I would JOIN twice on the different columns and let the projection-clause handle it:
SELECT
*
FROM
Inbox
INNER JOIN [User] AS u ON i.FromUserId = u.UserId
LEFT OUTER JOIN Merchandise AS userMerchant ON u.MerchandiseId = userMerchant.MerchandiseId
LEFT OUTER JOIN Merchandise AS inboxMerchant ON Inbox.ToMerchantId = inboxMerchant .MerchandizeId
WHERE
Inbox.ToCompanyId = 10
OR
Inbox.FromCompanyId = 10
This can then be translated into Linq using the LEFT OUTER JOIN approach ( How to implement left join in JOIN Extension method )
Note that if you're using Entity Framework then you don't need to worry about doing any of this at all! Just use Include:
var query = db.Inbox
.Include( i => i.User )
.Include( i => i.User.Merchandise )
.Include i => i.Merchandise )
.Where( i => i.ToCompanyId = 10 || i.FromCompanyId == 10 );
I have following values in a table and I need a LINQ query which satisfies the conditions below.
Select a single record, which has the maximum RevOrder.
If there are multiple records with same maximum RevOrder, then select the record with maximum RevDate
If RevDate's are equal too then get the record with maximum RevisionStatusID.
RevisionStatusID RevDate RevOrder
1 12/01/2012 0
2 14/02/2013 1
3 10/02/2013 2
4 11/01/2013 2
5 11/01/2013 3
I tried the below query but it gives an error.
var DocRevIDs = (from tbh in context.tblDocumentHeaders
join tbr in context.tblDocumentRevisions
on tbh.DocumentHeaderID equals tbr.DocumentHeaderID
where tbh.DocumentHeaderID == tb.DocumentHeaderID
select tbr).Max(o => new { o.RevOrder,o.RevisionDate,o.DocumentRevisionID });
Unable to process the type
'<>f__AnonymousType523[System.Nullable1[System.Double],System.Nullable`1[System.DateTime],System.Int32]',
because it has no known mapping to the value layer.
Instead of trying to use Max(), use a combination of orderby clauses and then use FirstOrDefault(). This would be the approach you would take to do it via straight SQL.
So that would give you something similar to:
var docRevision = (from tbh in context.tblDocumentHeaders
join tbr in context.tblDocumentRevisions
on tbh.DocumentHeaderID equals tbr.DocumentHeaderID
where tbh.DocumentHeaderID == tb.DocumentHeaderID
orderby tbr.RevOrder descending, tbr.RevisionDate descending, tbr.DocumentRevisionID descending
select tbr).FirstOrDefault();
you should Order the records in the linq:
var DocRevIDs = (from tbh in context.tblDocumentHeaders
join tbr in context.tblDocumentRevisions
on tbh.DocumentHeaderID equals tbr.DocumentHeaderID
where tbh.DocumentHeaderID == tbr.DocumentHeaderID
orderby tbr.RevOrder descending, tbr.RevDate descending, tbr.RevisionStatusID descending
select tbr).First();
Good Evening everyone,
I've been trying to figure out the most efficient way to do this, but am falling short. Here's how it goes...
I am ultimately trying to determine "like customers" based on a specific customer's buying habits and a given threshold, say 50%. IE customer 1 purchased products A,B,C,D ... customer 2 purchased B,C,D,E ... these two customers are >= 50% "likeness" so they should be matched.
My schema is as would be expected
CLIENT (1 ----- many) CLIENT_PURCHASE (1 -------many) PRODUCT
*clientID *clientID *prodID *prodID
For now I am ignoring the threshold and simply am trying to find customers who have purchased any item within customer 1's history. I think I have this working with the following two queries:
var clientOneHistory = (from cp in client.Client_Purchase
select cp.prodID).ToList();
var matchedClients = (from cp in db.Client_Purchase
where clientOneHistory.Contains(cp.prodID)
select cp.Client.fullname).Distinct().ToList();
So my ultimate question is, "How do I work in the threshold portion?"
Thanks for your time
I'm not sure exactly how to form these queries for your particular case. Assuming you want to use LINQ-to-SQL. Instead, I'll use the NorthWind database as an example to do the same thing. You could then use the ideas used here in your implementation. I don't think it's possible to do this completely using LINQ-to-SQL, you'll have to do a mix.
var threshold = 0.5M;
// let's pick a customer id
var myId = "VINET";
// get products of current customer
var myProducts = (from c in db.Customers
where c.CustomerID == myId
join o in db.Orders on c.CustomerID equals o.CustomerID
join od in db.Order_Details on o.OrderID equals od.OrderID
select od.ProductID)
.Distinct()
.ToArray();
// get the products of all other customers
var others = (from c in db.Customers
where c.CustomerID != myId
join o in db.Orders on c.CustomerID equals o.CustomerID
join od in db.Order_Details on o.OrderID equals od.OrderID
group od.ProductID by c.CustomerID into g
select new { CustomerID = g.Key, Products = g.Distinct() })
.AsEnumerable();
// calculate "likeness" values for each person
var likeness = from o in others
let Percent = Decimal.Divide(myProducts.Intersect(o.Products).Count(), myProducts.Length)
where Percent >= threshold
select new { o.CustomerID, Percent };