Ranking. Linq to sql question - c#

I have a table of orders made by persons:
Orders
{
Guid PersonId,
int Priority,
Guid GoodId
}
Priority is some integer number. For example:
AlenId 1 CarId
DianaId 0 HouseId
AlenId 3 FoodId
DianaId 2 FlowerId
I want to retrieve highest priority orders for each person:
AlenId 1 CarId
DianaId 0 HouseId
In T-SQL I'll use ranking, how can I get the same result in Linq-2-Sql ?
Thank you in advance!

Something like this:
var query = from order in context.Orders
orderby order.Priority
group order by order.PersonId into personOrders
select personOrders.First();
I believe that should work, but I don't know how well-defined it is, in terms of the ordering post-grouping. This would also work, although it's slightly uglier:
var query = from order in context.Orders
group order by order.PersonId into personOrders
select personOrders.OrderBy(order => order.Priority).First();
Or using just dot notation:
var query = context.Orders
.GroupBy(order => order.PersonId)
.Select(group => group.OrderBy(order => order.Priority)
.First());

Related

Get list of items where their ID is equal to some Values - linq c#

Here is a query:
from order in db.tblCustomerBuys
where selectedProducts.Contains(order.ProductID)
select order.CustomerID;
selectedProducts is a list containing some target products IDs, for example it is { 1, 2, 3}.
The query above will return customerIDs where they have bought one of the selectedProducts. for example if someone has bought product 1 or 2, its ID will be in result.
But I need to collect CustomerIDs where they have bought all of the products. for example if someone has bought product 1 AND 2 AND 3 then it will be in result.
How to edit this query?
the tblCustomerBuys are like this:
CustomerID - ID of Customer
ProductID - the product which the customer has bought
something like this:
CustomerID ProdcutID
---------------------------
110 1
110 2
112 3
112 3
115 5
Updated:
due to answers I should do grouping, for some reason I should use this type of query:
var ID = from order in db.tblCustomerBuys
group order by order.CustomerID into g
where (selectedProducts.All(selProdID => g.Select(order => order.ProductID).Contains(selProdID)))
select g.Key;
but it will give this error:
Local sequence cannot be used in LINQ to SQL implementations of query operators except the Contains operator.
The updated query is the general LINQ solution of the issue.
But since your query provider does not support mixing the in memory sequences with database tables inside the query (other than Contains which is translated to SQL IN (value_list)), you need an alternative equivalent approach of All method, which could be to count the (distinct) matches and compare to the selected items count.
If the { CustomerID, ProductID } combination is unique in tblCustomerBuys, then the query could be as follows:
var selectedCount = selectedProducts.Distinct().Count();
var customerIDs =
from order in db.tblCustomerBuys
group order by order.CustomerID into customerOrders
where customerOrders.Where(order => selectedProducts.Contains(order.ProductID))
.Count() == selectedCount
select customerOrders.Key;
And if it's not unique, use the following criteria:
where customerOrders.Where(order => selectedProducts.Contains(order.ProductID))
.Select(order => order.ProductID).Distinct().Count() == selectedCount
As your question is written, it is a bit difficult to understand your structure. If I have understood correctly, you have an enumerable selectedProducts, which contains several Ids. You also have an enumeration of order objects, which have two properties we care about, ProductId and CustomerId, which are integers.
In this case, this should do the job:
ver result = db.tblCustomerBuys.GroupBy(order => order.CustomerId)
.Where(group => !selectedProducts.Except(group).Any())
.Select(group => group.Key);
What we are doing here is we are grouping all the customers together by their CustomerId, so that we can treat each customer as a single value. Then we are treating group as a superset of selectedProducts, and using a a piece of linq trickery commonly used to check if one enumeration is a subset of another. We filter db.tblCustomerBuys based on that, and then select the CustomerId of each order that matches.
You can use Any condition of Linq.
Step 1 : Create list of int where all required product id is stored
Step 2: Use Any condition of linq to compare from that list
List<int> selectedProducts = new List<int>() { 1,2 } // This list will contain required product ID
db.tblCustomerBuys.where(o=> selectedProducts .Any(p => p == o.ProductID)).select (o=>o.order.CustomerID); // This will return all customerid who bought productID 1 or 2

Linq select in select

I am struggling with converting MySQL query to linq syntax in C# (for use of Entity Framework). MySQL query looks like this:
SELECT *
FROM Availability as tableData
WHERE ID = (
SELECT Availability.ID
FROM Availability
WHERE Availability.FrameID = tableData.FrameID
ORDER BY Availability.Date DESC limit 1)
I don't know how to convert this part FROM table AS someName.
So far the only solution I have, is to execute raw SQL query such as:
dataContext.Availability.SqlQuery("SELECT * FROM Availability as tableData WHERE ID = (SELECT ID FROM Availability WHERE FrameID = tableData.FrameID ORDER BY Availability.Date DESC limit 1)").ToArray();
But it would be nice to know if linq can provide such a query.
Thanks in advance, for your answers!
If you need only latest record for every frame id, then use grouping:
dataContext.Availability
.GroupBy(a => a.FrameID)
.Select(g => g.OrderByDescending(a => a.Date).FirstOrDefault());
This query produces required result, though generated sql will be a little different. It will look like
SELECT /* limit1 fields */
FROM (
SELECT DISTINCT tableData.FrameID
FROM Availability as tableData) AS distinct1
OUTER APPLY (
SELECT TOP(1) /* project1 fields */
FROM (SELECT /* extent1 fields */
FROM Availability AS extent1
WHERE Availability.FrameID = distinct1.FrameID) AS project1
ORDER BY project1.Date DESC) AS limit1
NOTE: First() extension is not supported by EF
Take all the Avilabilities, group by FrameId, order each group by date, take the first entry of each group.
The ToList() at the end fetches all the results and puts them in a List.
var tableDate = dataContext.Availability
.GroupBy(x => x.FrameId)
.Select(x => x.OrderByDescending(y => y.Date).FirstOrDefault())
.ToList();
Yes Linq can do this, but you need to have a starting sequence on which the linq should operate. Usually this sequence has the same type as your table, in your case Availability.
From your sql I gather that each record in the Availabilities table has at least properties Id, FrameId and Date:
class Availability
{
public int Id {get; set;}
public int FrameId {get; set;
public DateTime Date {get; set;}
}
Of course this can also be an anonymous type. The importance is that you have somehow a sequence of items having these properties:
IQueryable<Availability> availabilities = ...
You wrote:
I need only one record (with max Date of insert) for every FrameID
So every Availability has a FrameId, and you want for every FrameId the record with the highest Date value.
You could use Enumerable.GroupBy and group by FrameId
var groupsWithSameFrameId = availabilities.GroupBy(availability => availability.FrameId);
The result is a sequence of groups. Every group contains the sequence of all availabilities with the same FrameId. In other words: if you take a group, you'll have a group.Key with a FrameId value and a sequence of all availabilities that have this FrameId value.
We won't use the group.Key.
If you sort the sequence of elements in each group in descending order by Date and take the first element, you'll have the date with the highest value
var recordWithMaxDateOfInsert = groupsWithSameFrameId
.Select(group => group.OrderByDescending(groupElement => groupElement.Date)
.First();
From every group sort all elements of the group by descending Date value and take the first element of the sorted group.
Result: from your original availabilities, you have for every frameId the availability with the highest value for date.

Entity framework distinct but not on all columns

I'd like to make a query through entity framework that unions contacts from two different tables, remove duplicates and orders by date. The issue I'm having is around the different dates making the same contact appear as unique. I don't want to include the date in the distinct but I do need it afterwards for the ordering. I can't do the ordering first, remove the date and then perform the distinct, because the distinct changes the ordering. Neither can I order before the union because that doesn't ensure ordering after the union.
I would like to distinct all fields except the date, which is only required for the ordering.
Ideally I would pass a comparer to the distinct but EF can't translate this to SQL.
db.Packages.Select(p => new Recent()
{
Name = p.Attention, Address1 = p.Address1, ... , Date = ShippingDate
})
.Concat(db.Letters.Select(l => new Recent()
{
Name = l.AddressedTo, Address1 = p.Address1, ..., Date = MarkedDate
})
.Distinct()
.OrderByDescending(r => r.Date);
OR the problem in SQL
SELECT DISTINCT Attention, Address1, ShippingDate
FROM Packages
UNION ALL
SELECT AddressedTo, Address1, MarkedDate
FROM Letters
ORDER BY ShipmentDate DESC
You should be able to use a GroupBy to do what you want, like so (not to mention Group By is more performant than Distinct in EF):
db.Packages.Select(p => new Recent()
{
Name = p.Attention, Address1 = p.Address1, ... , Date = ShippingDate})
.Concat(db.Letters.Select(l => new Recent()
{
Name = l.AddressedTo, Address1 = p.Address1, ..., Date = MarkedDate}))
.GroupBy(p => <parameters to group by - which make the record distinct>)
.Select(g => new {Contact = g.Key, LastShippingDate = g.Max(p => p.ShippingDate)});
I'd be concerned with this approach, even if it was possible distinct would then remove one of the items and leave you with random date out of the two, and then your sort would be totally unpredictable.

How can I get the count in linq?

I have table called products with columns:
productid ,
productname,
productprice
categoryid
My problem is I want to get the number of products depending on product name along with details. I want to show the data in DataGridView. How can I know the number of products for a single product name like below?
productid productname productavailable productprice
--------------------------------------------------------------------------
1 product A 2 products(product A) 100
2 product B 5 Products(product B) 200
Like the above table I have to display in DataGridView. I am using LINQ and C# and my DbContext name is tsgdbcontext.
Use GroupBy with a key that contains your grouping properties. Then select out the key properties along with the count of each from the grouping.
var query = tsgdbcontext.Products
.GroupBy(p => new {
p.ProductId,
p.ProductName,
p.ProductPrice
})
.Select(g => new {
g.Key.ProductId,
g.Key.ProductName,
g.Key.ProductPrice,
Available = g.Count()
});
Not sure I am understanding exactly + making some assumptions but here is an example linq query that produces a count based on some arbitrary selection criteria (id=2 and price greater than 100)...
int count = (from p in tsgdbcontext.Products
where p.productid == 2 && p.productprice > 100
select p).Count();

Linq query to display a proper sort order

Table 1: Lookups
LookUpID
LookUpName
Desc
DisplayOrder
Table 2: BillingRates
BillingRateID
BillingRate
ClientID
LookupID
I want the lookup name to be displayed (sort by Bill rate)
DataContext DataContext1 = new DataContext1(AppSettings.ConnectionString);
return ( from Lookups in DataContext1.Lookups
join BillingRates in DataContext1.BillingRates
on Lookups.LookupID equals BillingRates.LookupID
orderby BillingRates.BillingRate
select new
{
Lookups.LookupID,
Lookups.LookupName,
Lookups.Desc
}).Distinct();
It gave me all the row, so I used Distinct(); The lookup Name is still not based on billing rate.
I am new to LINQ. Any pointers would be appreciated.
Why not just do the OrderBy at the end?
return (from Lookups in DataContext1.Lookups
join BillingRates in DataContext1.BillingRates
on Lookups.LookupID equals BillingRates.LookupID
select new
{
Lookups.LookupID,
Lookups.LookupName,
Lookups.Desc,
BillingRates.BillingRate
})
.GroupBy(x => x.LookupID)
.Select(y => y.OrderByDescending(x => x.BillingRate).First())
.OrderByDescending(x => x.BillingRate);
EDIT: I am kind of confused but try the following and let me know if that helps.
First of all, if you have a foreign key relationship set up, LINQ will create the join for you automatically, so it would be just:
DataContext1.Lookups.Max(LkUp => LkUp.BillingRate.BillingRate)
Otherwise, (with the explicit join)
return ( from Lookups in DataContext1.Lookups
join BillingRates in DataContext1.BillingRates
on Lookups.LookupID equals BillingRates.LookupID
orderby BillingRates.BillingRate desc
select new
{
Lookups.LookupID,
Lookups.LookupName,
Lookups.Desc,
BillingRates.BillingRate
}).First();

Categories

Resources