entity framework orderby sum slow performance - sql

I have a repository table and add any transaction of products here like this:
productID qty:
103 2
103 -1
in the view of my products I want to show products order by sum of the qty of products > 0
so I write this :
dbContext.tbl_Product.OrderByDescending(n => n.tbl_repository.Sum(x => x.Qty) > 0).ThenByDescending(m => m.ID);
but the performance of this is too slow, is there any other way to make it speedy ?

You can try leveraging the GroupBy method.
First create an (optional) class to hold the results:
public class ProductGroup
{
public string ProductID { get; set; } // or int, whatever your product id's type is
public int QuantitySum { get; set; }
}
I'm not sure how your repository is implemented, but try querying the context directly first and see if you get better results:
using (var db = new ApplicationContext())
{
var products = db.Products
.GroupBy(product => product.ProductID)
.Where(productGroup => productGroup.Sum(p => p.Quantity) > 0)
.Select(productGroup => new ProductGroup { ProductID = productGroup.Key, QuantitySum = productGroup.Sum(product => product.Quantity) })
.OrderByDescending(product => product.ProductID)
.ToList();
// 'products' should be a list of ProductGroup items holding the ID and Sum.
}

Related

Filter values based on custom attribute in another table

I have two tables DefaultAttributes and CustomAttributes.
DefaultAttributeTable:
1. Id
2. Product
4. Description
CustomAtrributeTable:
1. Id
2. DefaultAttributeMappingId(FK from DefaultAttributeTable)
3. CustomAtrributeName
4. CustomeAttributeValue
Entries made by user:
user update the values Product -> Vegetables, Description -> House Hold Item, IsOrganic(Custom Attribute created) -> True and IsDisposable(Custom Attribute created) -> True
user update the values Product -> Fruits, Description -> House Hold Item, IsOrganic(Custom Attribute created) -> True and IsDisposable(Custom Attribute created) -> True
user update the values Product -> Plastic, Description -> House Hold Item, IsOrganic(Custom Attribute created) -> False and and IsDisposable(Custom Attribute created) -> False
Then the values will be updated in the table will
DeafaultAtrributeTable:
CustomAttributeTable:
I want to combine the two tables and select the Id, product, IsOrganic, IsDisposable and filter the values based on the isorganic column. Also the custom attribute column name should be taken form the CustomAtrributeTable. Please suggest to me how to achieve it in SQL and Linq Query. The filtered value should be
You may try this in SQL
select DA.Id, DA.Product, CA.CustomeAttributeValue as IsOrganic
from DeafaultAtrributeTable as DA inner join CustomAttributeTable as CA
on DA.Id = CA.DefaultAttributeMappingId
In LINQ
var query =
from DA in DeafaultAtrributeTable
join CA in CustomAttributeTable on DA.ID equals CA.DefaultAttributeMappingId
where CA.CustomeAttributeValue == true
select new { Id = DA.Id, Product = DA.Product, IsOrganic = CA.CustomeAttributeValue };
or LINQ extension method is
var query = DeafaultAtrributeTable // your starting point - table in the "from" statement
.Join(CustomAttributeTable , // the source table of the inner join
DA => DA.ID, // Select the primary key (the first part of the "on" clause in an sql "join" statement)
CA => CA.DefaultAttributeMappingId, // Select the foreign key (the second part of the "on" clause)
(DA, CA) => new { Id = DA.Id, Product = DA.Product, IsOrganic =
CA.CustomeAttributeValue }) // selection
.Where(RES => RES.CA.CustomeAttributeValue == true); // where statement
try this
public class Filtered
{
public int ProductId { get; set; }
public string ProductName { get; set; }
public bool? IsOrganic { get; set; }
public bool? IsDisposable { get; set; }
}
var result = defaultAttributeTable.Join(customAtrributeTable, DAT => DAT.Id, CAT => CAT.DefaultAttributeTableId, (DAT, CAT) =>
new Filtered
{
//your join with null values for opposing isdisposable and isorganic
ProductId = DAT.Id,
IsDisposable = CAT.CustomAtrributeName == "IsDisposable" ? (bool?)CAT.CustomeAttributeValue : null,
IsOrganic = CAT.CustomAtrributeName == "IsOrganic" ? (bool?)CAT.CustomeAttributeValue : null,
ProductName = DAT.Product
}).GroupBy(q => q.ProductId) //group it by productid
.Select(q =>
new Filtered
{
//this will flatten opposing isorganic and isdisposable
ProductId = q.Key,
IsDisposable = q.First(e => e.IsDisposable.HasValue).IsDisposable.Value,
IsOrganic = q.First(e => e.IsOrganic.HasValue).IsOrganic.Value,
ProductName = q.First(e => e.ProductId == q.Key).ProductName
}).ToList();

Create relation hasMany() with composite field

Have the relation in model Product :
public function getDiscount()
{
return $this->hasMany(Discount::className(), ['id' => 'available_discount']);
}
Model has field available_discount, that stores data as 1;2;3, where 1;2;3 is discount ids.
Query Product::find()->joinWith('discount d')->where(['d.id' => [1,2,3]])->all()return products with key discount = [].
How i can return discounts as relation with ID 1, 2, 3 ?
Try this:
public function getDiscounts()
{
$idsAsArray = explode(';', $this->available_discount);
$query = Discount::find()->where(['id' => $idsAsArray]);
$query->multiple = true;
return $query;
}
And then get them via:
$product->discounts; // returns Discount[]
$product->getDiscounts()->count(); // gets the count of discount models.

RavenDB: How do I fix this Map Reduce code? I'm getting NULL values where I'm expecting Results

I have the following code:
public class WorkOrderByUserId : AbstractMultiMapIndexCreationTask<WorkOrderByUserId.Result>
{
public WorkOrderByUserId()
{
this.AddMap<WorkOrder>(items
=> from item in items
select new Result
{
OwnerId = item.OwnerId,
WorkOrder = new WorkOrderLookupDto
{
Id = item.Id,
Name = item.EventName
},
WorkOrders = null
});
this.AddMap<Invoice>(items
=> from item in items
select new Result
{
OwnerId = item.WorkOrder.OwnerId,
WorkOrder = new WorkOrderLookupDto
{
Id = item.WorkOrder.Id,
Name = item.WorkOrder.EventName
},
WorkOrders = null
});
this.Reduce = results => from result in results
group result by result.OwnerId
into g
select new Result
{
OwnerId = g.Key,
WorkOrders = g.Select(x => x.WorkOrder),
WorkOrder = null
};
this.Indexes.Add(x => x.OwnerId, FieldIndexing.Default);
}
public class Result
{
public string OwnerId { get; set; }
public IEnumerable<WorkOrderLookupDto> WorkOrders { get; set; }
public WorkOrderLookupDto WorkOrder { get; set; }
}
}
It gets me very close to where I want to be, however I seem to be missing something and I'm losing information and I'm not sure why.
Using the Map/Reduce Visualizer I notice the MAP is displaying the Results (i.e. WorkOrders is populated, and WorkOrder is null)
As this point I was expecting to have a bunch of items with NULL WorkOrders and a valid WorkOrder, which I expected to reduce down into the WorkOrders collection.
When I look at the final reduce in the visualizer, I notice my WorkOrder is NULL and my WorkOrders are missing entirely.
And when I look at the FINAL result of the Index I see what I'm looking for, just without the actual data I'm looking for.
What do I need to change to get my final WorkOrders to NOT be NULL?
I was able to get my desired result using the following code:
public class WorkOrderByUserId : AbstractMultiMapIndexCreationTask<WorkOrderByUserId.Result>
{
public WorkOrderByUserId()
{
this.AddMap<WorkOrder>(items
=> from item in items
select new Result
{
OwnerId = item.OwnerId,
WorkOrders = new[]
{
new WorkOrderLookupDto
{
Id = item.Id,
Name = item.EventName
}
}
});
this.AddMap<Invoice>(items
=> from item in items
select new Result
{
OwnerId = item.WorkOrder.OwnerId,
WorkOrders = new[]
{
new WorkOrderLookupDto
{
Id = item.WorkOrder.Id,
Name = item.WorkOrder.EventName
}
}
});
this.Reduce = results => from result in results
group result by result.OwnerId
into g
select new Result
{
OwnerId = g.Key,
WorkOrders = g.SelectMany(x => x.WorkOrders)
};
this.Indexes.Add(x => x.OwnerId, FieldIndexing.Default);
}
public class Result
{
public string OwnerId { get; set; }
public IEnumerable<WorkOrderLookupDto> WorkOrders { get; set; }
}
}

NHibernate Projections (QueryOver.SelectList) limits

I'm new on stackoverflow and i hope this question will be appreciated.
Simply said: I select everything from table x left outer join table y. Table x has way too many columns so i make new object x. This object is used for the Projection. I can project every single column of table x i want. But when i try to project/select an collection (the collection of table y) then i get the same error: 'Index was outside the bounds of the array'.
My question: Does NHibernate support to select/project a collection at all? Because i've seen this question multiple times by searching on google (and stackoverflow), but none of those questions were answered.
Code example:
public class X
{
public virtual int ID { get; set; }
public virtual int IDontNeedMoreInfoAboutClassXItTakesToMuchTimeToRetrieve { get; set; }
public virtual IList<Y> YCollection { get; set; }
}
public class Y
{
public virtual int YID { get; set; }
}
public class XRepository{
public ISession Session {get; set;}
public IList<X> Get()
{
X xAlias = null;
Y yAlias = null;
X resultAlias = null;
return Session.QueryOver<X>()
.JoinAlias(() => xAlias.YCollection, () => yAlias, JoinType.LeftOuterJoin)
.SelectList(list => list
.SelectGroup(() => xAlias.ID).WithAlias(() => resultAlias.ID)
.SelectGroup(() => xAlias.YCollection).WithAlias(() => resultAlias.YCollection)) // Index was outside the bounds of the array
.TransformUsing(Transformers.AliasToBean<X>()).List<X>();
}
}
the results returned have to be grouped with the contents intact (so sql is out since it can only aggregate over the grouped items) but NHibernate can only do this for m,apped entities because there it knows the identity to group by. It is simple to do it manually
Y yAlias = null;
var tempResults = Session.QueryOver<X>()
.JoinAlias(x => x.YCollection, () => yAlias, JoinType.LeftOuterJoin)
.SelectList(list => list
.Select(() => xAlias.Id)
.Select(() => xAlias.Name)
...
.Select(() => yAlias.Id)
...
.ToList<object[]>()
List<X> results = new List<X>(100); // switch to Hashset if too slow and order does not matter
foreach(var row in tempResults)
{
Y y = new Y { Id = (long)row[4], ... };
X x = results.FirstOrDefault(x => x.Id == (long)row[0]));
if (x != null)
x.YCollection.Add(y);
else
results.Add(new X { Id = (long)row[0], ..., YCollection = { y } };
}
return results;

Linq to SQL: Get top 10 most ordered products

I'm wanting to grab the 10 most ordered products. My tables look similar to this:
ProductProductID | ProductName
OrderedProductProductID | OrderID
OrderOrderID | DateOrdered
At the moment I've got the following:
return (from product in db.Products
from orderedProduct in db.OrderedProducts
where orderedProduct.ProductID == product.ProductID
select product).OrderByDescending(???).Distinct().Take(10);
I've noted in the above query where I'm uncertain of what to put. How do I orderby the number of products that appear in the ordered products table?
return (from product in db.Products
from orderedProduct in db.OrderedProducts
where orderedProduct.ProductID == product.ProductID
group orderedProduct by product into productGroups
select new
{
product = productGroups.Key,
numberOfOrders = productGroups.Count()
}
).OrderByDescending(x => x.numberOfOrders).Distinct().Take(10);
It will give you 10 items, each item contains product object, and numberOfOrders integer.
Edit:
Since this will be as a return value for a method, and since C# doesn't allow returning an anonymous type (yet .. this feature is in C# 4.0), you convert the anonymous type into a class.
Create a class of the type you want to return
public class ProductOrders
{
public ProductOrders() {
}
public Product product { get; set; }
public int numberOfOrders { get; set; }
}
and Use this query to return objects of that class
return (from product in db.Products
from orderedProduct in db.OrderedProducts
where orderedProduct.ProductID == product.ProductID
group orderedProduct by product into productGroups
select new ProductOrders
{
product = productGroups.Key,
numberOfOrders = productGroups.Count()
}
).OrderByDescending(x => x.numberOfOrders).Distinct().Take(10);
The return value now is of type IEnumerable<ProductOrders>.