Linq query to order by on multiple related data - linq

Consider the below tables with the data
Customers -> Orders -> Items
Customers ->
A
B
C
Orders ->
o1 - A,
o2 - A,
o3 - B,
o4 - C
OrderItems ->
o1 - Item1,
o1 - Item2,
o2 - Item3,
o3 - Item2,
o4 - Item1
Item ->
Item1,
Item2,
Item3,
Item4
We have a similar mapping as above in our DB.
Now in linq i would like to get List of Customers sorted by Items which are comma seperated
eg:
Customer Items
C Item1
A Item1, Item2
B Item2
Ive tried something like this
Customer.OrderBy( cust => string.Join(",", cust.Orders
.SelectMany( order=>order.OrderItems)
.Select( orderItem=> orderItem.Item.Name)
.OrderBy(item=>item)));
but string.Join is not allowed inside linq statements..
Its not required to display the Items in my grid, but i need to get customers sorted by the comma separated Items..
And also i dont want this to be done in the UI level as the sorting needs to be done on IQueryable customer object to which other filters are added and then executed later ..
A linq orderby query with IQueryable Customer object, returning an IQueryable object.

When you are querying the Customer collection, your query will be converted to sql instructions. Since you mentioned other filters, I suppose it's a big table and you don't want to display / get all rows.
Even the string.Join operator were allowed in EF, your order clause operates on an complex statement that needs to be processed for each row to determine the correct result order (see this question for an example of what your string.join would do).
You need to somehow simplify your order clause or store the item list string into an sql field. If you can't store this string, you can try to create a stored procedure, a view or process the data using linq to objects (in this case, apply all filters using EF, get the results using .ToList() and apply your order filter using regular linq to objects).
Be aware that if you don't simplify your query and the customer table is big, you'll face some performance issues.

Related

Pig Latin JOIN error

I am loading two datasets A, B
A= LOAD [datapath]
B= LOAD [datapath]
I want to JOIN all fields of both A and B by id field.Both A and B have common field id and other fields. When I perform JOIN by id:
AB= JOIN A by id, B by id;
The resulted dataset AB includes two similar columns for the field id, However, it only must show only one column for the id field. What am I doing wrong here?
That's the expected behaviour, when joining two datasets, all columns are included (even those ones which you are joining by)
You can check it here
If you want to drop a column you can do it with the generate statement. But first you ned to know the position of the undesired column.
If that column is, for instance, in the 3th position
C = FOREACH AB GENERATE $1,$2, $4, $5...;
Edit from the comments
You can also use a generate statement without knowing position. Example:
C = FOREACH AB GENERATE A::id AS id, A::foo AS foo, B::bar AS bar;

Linq query for one to many

My question involves MVC + Linq query. I will try to make it simple without going into the details of the Model, View, etc.. Say I have 2 tables T1 & T2. T1 holds restaurants details & T2 holds restaurants image paths. T2 rows contain restaurantID. Now if T2 has more than one rows of image paths for a Restaurant and I only need the first image path from T2 in the linq query how would I form such query? I tried to simplify the question as in fact I have 6 table joins related to the Restaurants in the query. I formed a view model which only contains the fields I want to display. I am trying to populate the view model in the controller & the query is in the controller obviously.
When I join T2 to the query, I get all the Restaurants details together with the images. But the view repeats the same Restaurant as many times as the number of table rows in T2 which is not what I want. This is the problem from the way I set the query. The query uses joins. I only need the first row from T2 while I get all from the Restaurant details. I failed to find an example for such requirement on the web so far. Your directions will be much appreciated.
Serhat Albayoglu
On your join you can use an into and then in the select you can select the FirstOrDefault
var query = from t in context.T1
join t2 in context.T2 on t.Id equals t2.RestaurantID into tgroup
select
{
t2.FirstOrDefault().path
};

Linq query that results in one field in parent entity being a collection of child entities

I am trying to write a Linq query that results in a parent entity with one of the fields being a collection of entities of its related children. For example, I have a collection of all customers entities and a collection of all orders entities. The orders entities have a field called customerPK which contains the link to the related parent customer entity. I want to create a Linq query that joins the two collections and results in all the fields of the customer entity plus an additional field which is the collection object of all the related order entities for that specific customer entity.
Hopefully this should do the trick;
EDIT: Code updated to perform a Left Outer Join, based on this example; http://smehrozalam.wordpress.com/2009/06/10/c-left-outer-joins-with-linq/. This now includes Customers who have no orders.
var query = from c in customers
join o in orders on c.ID equals o.CustomerPK into joined
from j in joined.DefaultIfEmpty()
group j by c into g
select new { Customer = g.Key, Orders = g.Where(x => x != null) };
Note, the use of Where on the selection of the Orders grouping is so that null orders are filtered out at this point, instead of ending up with a grouping containing a single null Order for customers who don't have orders.
Then some example usage;
foreach (var result in query)
{
Console.WriteLine("{0} (ID={1})", result.Customer.Name, result.Customer.ID);
foreach (var order in result.Orders)
{
Console.WriteLine(order.Description);
}
}
This example results in an object with two fields, the Customer and then a group of related Orders, but there's no reason why you can't select the individual fields of your customer object in the query as you specified in your post.

Entity Framework 4 generated queries are joining full tables

I have two entities: Master and Details.
When I query them, the resulting query to database is:
SELECT [Extent2]."needed columns listed here", [Extent1]."needed columns listed here"
FROM (SELECT * [Details]."all columns listed here"...
FROM [dbo].[Details] AS [Details]) AS [Extent1]
LEFT OUTER JOIN [dbo].[Master] AS [Extent2] ON [Extent1].[key] = [Extent2].[key]
WHERE [Extent1].[filterColumn] = #p__linq__0
My question is: why not the filter is in the inner query? How can I get this query? I've tried a lot of EF and Linq expressions.
What I need is something like:
SELECT <anything needed>
FROM Master LEFT JOIN Details ON Master.key = Details.Key
WHERE filterColumn = #param
I'm having a full sequential scan in both tables, and in my production environment, I have milions of rows in each table.
Thanks a lot !!
Sometimes The entity Framework does not produce the best query. You can do a few of the following to optimize.
Modify the linq statement (test with
LINQPad)
Create a stored proc and map the stored proc to return an entity
Create a view that handles the join and map the view to a new
entity

Linq to SQL: order by value in related table

I have 2 tables which in simplified form look like this:
Products(
id: int,
name: varchar
);
ProductSpecs(
product_id: int,
spec_name: varchar,
spec_value: int
);
Now I need to sort products (in linq to sql) by value of some specification item (eg. "price"). So I do something like this
var products = from p in db.Products
from ps in p.ProductsSpecs
where ps.spec_name == "price"
orderby ps.spec_value
select p;
The problem is that if there's no such ProductSpec with spec_name "price" the product is not included at all. I can add these products with Union or Concat but this way the sorting of the first part is not preserved.
What is the best way to deal with this?
Thanks.
First, I would recommend that you either do this in pure SQL as a function or Stored Procedure and then access this through linq, or add a price column to your product table. It seems like price would be a normal attribute to add to all of your products even if that price is NULL.
SQL:
select p.*
from products p
left outer join productspecs ps on
p.id = ps.product_id
and ps.spec_name = 'Price'
order by ps.spec_value
With that said, here's the weird bit of LINQ that should work on your table (I might have some of the column names spelled incorrectly):
var products = from p in db.Products
join ps in (from pss in db.ProductSpecs
where pss.spec_name== "Price"
select pss
) on p.id equals ps.product_id into temp
from t in temp.DefaultIfEmpty()
orderby t.spec_value
select p;
I tested this on some tables setup like above and created 5 products, three with prices in different value orders and this LINQ ordered them just like the SQL above and returned the null result rows as well.
Hope this works!
In ordinary SQL, you'd use an LEFT OUTER JOIN. This preserves rows that appear in the left-hand table (the one listed first), even when there's no matching row in the right-hand table (the second one listed, and the one that is outer joined). You end up with nulls for the values that should be, but weren't, present in the right-hand table. So, the price for those items missing a price would appear as NULL.
What that translates to in LINQ to SQL is another matter.
You might care to think about whether it is reasonable to have products that do not have a price. You're emulating something called EAV - Entity, Attribute, Value - tables, and they are generally regarded as 'not a good thing'.
Can you not just do a simple join?
var products =
from p in db.Products
join ps in db.ProductSpecs on p.id equals ps.product_id
where ps.spec_name == "price"
orderby ps.spec_value
select p;

Resources