LINQ to EF, Left Join and group by clause - c#

I have this SQL:
select o.prod_id, SUM(o.[count]) as [count]
into #otgr
from otgr o
where o.[date]<= #date
group by o.prod_id
select f.prod_id, SUM(f.[count]) as [count]
into #factory
from factory f
where f.[date]<= #date
group by f.prod_id
select p.name, p.id, f.[count] - ISNULL(o.[count],0) as av_count
from products p
join #factory f on f.prod_id = p.id
left join #otgr o on o.prod_id = p.id
where f.[count] - ISNULL(o.[count],0) > 0
How can I translate this into Linq? I'm stuck with this code:
from otgrr in db.otgr
where otgrr.date <= date
group otgrr by otgrr.prod_id into otgrs
from fac in db.factory
where fac.date <= date
group fac by fac.prod_id into facs
from prod in db.products
join fac2 in facs on prod.id equals fac2.Key
join otg2 in otgrs.DefaultIfEmpty(new {id = 0, av_count = 0 }) on prod.id equals otg2.Key
where (fac2.SUM(a=>a.av_count) - otg2.SUM(a=>a.av_count)) > 0
select new products { id = prod.id, name = prod.name, av_count = (fac2.SUM(a=>a.av_count) - otg2.SUM(a=>a.av_count))
Thank to everyone, and sorry for my bad english

You can also check LINQPad.
Of course, you can split this into multiple LINQ queries (after all, the execution is deferred, so it will be executed all as one single query, without using temporary tables. It should be faster in 99% of the cases).
But in your case it can be written more simply, by using navigation properties you probably have already set up:
var result= from p in products
select new {Name=p.Name,
Id = p.Id,
Count = p.Factories.Where(f=> f.date <= date).Sum(f=>f.Count)
- p.otgrs.Where(o=> o.date <= date).Sum(o=>o.Count)
};

Related

Multiple AND conditions on the same column [Servicestack.OrmLite]

I was wondering if it's possible to have multiple AND conditions on the same column using Servicestack.OrmLite. This is the SELECT statement I printed out, but It always returns 0. I should get the product count from the products having both specifications with id 1016 and 17.
SELECT COUNT(DISTINCT "Product"."Id")
FROM "Product"
INNER JOIN "ProductManufacturer"
ON ("Product"."Id" = "ProductManufacturer"."ProductId")
INNER JOIN "ProductSpecificationAttribute"
ON ("Product"."Id" = "ProductSpecificationAttribute"."ProductId")
WHERE ("ProductManufacturer"."ManufacturerId" = 6)
AND ("ProductSpecificationAttribute"."SpecificationAttributeOptionId" = 1016)
AND ("ProductSpecificationAttribute"."SpecificationAttributeOptionId" = 17)
A single column value can't possibly have two values at the same time.
What you want is either:
AND
(
ProductSpecificationAttribute.SpecificationAttributeOptionId = 1016
OR
ProductSpecificationAttribute.SpecificationAttributeOptionId = 17
)
Or, more succinctly:
AND
(
ProductSpecificationAttribute.SpecificationAttributeOptionId
IN (1016, 17)
)
And turn off whatever option is forcing your tool to "inject" "double" "quotes" "around" "every" "entity" "name" because it makes the query text unmanageable. You might also consider using aliases and schema prefixes, like INNER JOIN dbo.ProductSpecificationAttribute AS psa...
After further clarification... the goal is to find products where they have both of those attributes on different rows, which isn't clear from the description or the code ORMLite barfed out. Here's what you want in that case (there are several ways to do this, but converting everything to EXISTS also allows you to remove the DISTINCT from the COUNT, which is never free):
SELECT COUNT(Product.Id) FROM dbo.Product AS p
WHERE EXISTS
(
SELECT 1 FROM dbo.ProductManufacturer AS pm
WHERE pm.ProductId = p.Id AND pm.ManufacturerId = 6
)
AND EXISTS
(
SELECT 1 FROM dbo.ProductSpecificationAttribute AS psa
WHERE psa.ProductId = p.Id
AND psa.SpecificationAttributeOptionId = 1016
)
AND EXISTS
(
SELECT 1 FROM dbo.ProductSpecificationAttribute AS psa
WHERE psa.ProductId = p.Id
AND psa.SpecificationAttributeOptionId = 17
);
If ProductSpecificationAttribute is poorly index and this leads to two scans, you could change that by saying something like this (untested, but I'm happy to test it out if you can produce a db<>fiddle:
SELECT COUNT(Product.Id) FROM dbo.Product AS p
WHERE EXISTS
(
SELECT 1 FROM dbo.ProductManufacturer AS pm
WHERE pm.ProductId = p.Id
AND pm.ManufacturerId = 6
)
AND EXISTS
(
SELECT 1 FROM dbo.ProductSpecificationAttribute AS psa
WHERE psa.ProductId = p.Id
AND psa.SpecificationAttributeOptionId IN (17, 1016)
GROUP BY psa.ProductId, psa.SpecificationAttributeOptionId
HAVING COUNT(DISTINCT psa.SpecificationAttributeOptionId) > 1
);
It's also really weird that the table ProductManufacturer has a list of ProductIDs in it that point back to Product - usually Product would have a ManufacturerID that points in the other direction.
Anyway, you might consider using stored procedures that your ORM can call if it has problems creating queries beyond basic CRUD (which is unfortunately a limitation of all ORMs to some degree - they're great at the basics, covering 80% of the use case, but they're terrible at the other 20% - unfortunately most of us end up needing that other 20% before too long).
You can get all the product ids that you want if you group by product and set the conditions in the HAVING clause:
SELECT p.Id
FROM Product p
INNER JOIN ProductManufacturer pm ON p.Id = pm.ProductId
INNER JOIN ProductSpecificationAttribute psa ON p.Id = psa.ProductId
WHERE pm.ManufacturerId = 6 AND psa.SpecificationAttributeOptionId IN (17, 1016)
GROUP BY p.Id
HAVING COUNT(DISTINCT psa.SpecificationAttributeOptionId) = 2; -- both specifications must exist
If you want to count these products you could either use the above query as a subquery or a cte and count the rows:
WITH cte AS (
SELECT p.Id
FROM Product p
INNER JOIN ProductManufacturer pm ON p.Id = pm.ProductId
INNER JOIN ProductSpecificationAttribute psa ON p.Id = psa.ProductId
WHERE pm.ManufacturerId = 6 AND psa.SpecificationAttributeOptionId IN (17, 1016)
GROUP BY p.Id
HAVING COUNT(DISTINCT psa.SpecificationAttributeOptionId) = 2;
)
SELECT COUNT(*) FROM cte;
or, use COUNT() window function:
SELECT DISTINCT COUNT(*) OVER ()
FROM Product p
INNER JOIN ProductManufacturer pm ON p.Id = pm.ProductId
INNER JOIN ProductSpecificationAttribute psa ON p.Id = psa.ProductId
WHERE pm.ManufacturerId = 6 AND psa.SpecificationAttributeOptionId IN (17, 1016)
GROUP BY p.Id
HAVING COUNT(DISTINCT psa.SpecificationAttributeOptionId) = 2;

Having trouble with Group by and Join EF c#

I am having trouble converting this T-SQL code into EF C#
select
se.pcname, count(u.usrid) as total
from
tbusers as u
inner join
tbhcontainer as hc on u.hcid = hc.hcid
inner join
tbusersettings as se on hc.sid = se.sid
where
day(u.created) = 18
group by
se.pcname
order by
total desc
tbusers:
Username, PCName, Usrid, Created, HCID
tbhcontainer:
hcid, sid
tbusersettings:
sid, pcname
EDIT 1:
DateTime yesterday = DateTime.UtcNow.Date.AddDays(-1).AddHours(-3);
DB_121002_psmainEntities ctx = new DB_121002_psmainEntities();
var res = from r in ctx.tbusers
join hc in ctx.tbhcontainers on r.hcid equals hc.hcid
join s in ctx.tbUserSettings on hc.sid equals s.sid
group s by s.pcname
where r.created >= yesterday || r.created <= DateTime.Today
select r;
return res.Count();
It fails on all levels, just don't know how to use group by with joined tables
A direct translation would look more like this:
from u in ctx.Users
join hc in ctx.HContainers on u.Hcid equals hc.Hcid
join us in ctx.UserSettings on hc.Sid equals us.Sid
where u.Created.Day == 18
group u.Userid by us.Pcname into g
let total = g.Count()
orderby total descending
select new
{
pcname = g.Key,
total,
}
If you have additional clauses after the grouping, you need to place the results into another variable (g). Then you can access the group key and perform any aggregating function on that group.

How to convert store procedure to linq in nopCommerce c#

How to convert store procedure to linq in nopCommerce c#
My store procedure query
SELECT p.Id
FROM Product p WITH (NOLOCK)
LEFT JOIN Discount_AppliedToProducts dap WITH (NOLOCK)
ON p.Id = dap.Product_Id
LEFT JOIN Product_Category_Mapping pcm WITH (NOLOCK)
ON p.Id = pcm.ProductId
LEFT JOIN Discount_AppliedToCategories dac WITH (NOLOCK)
ON pcm.CategoryId = dac.Category_Id
AND dac.Category_Id IN (1 ,2 ,3 ,4 ,5 ,6)
LEFT JOIN Product_Manufacturer_Mapping pmm WITH (NOLOCK)
ON p.Id = pmm.ProductId
LEFT JOIN Discount_AppliedToManufacturers dam WITH (NOLOCK)
ON pmm.ManufacturerId = dam.Manufacturer_Id
WHERE dap.Discount_Id IN (3)
OR dac.Discount_Id IN (3)
OR dam.Discount_Id IN (3)
My linq query
var productlist = (from q in _productRepository.Table
select q).ToList();
var discount_AppliedToProductIds = (from dp in _discountRepository.Table
from p in dp.AppliedToProducts
select p).ToList().DistinctBy(d => d.Id).ToList();
var discount_AppliedToCategorieIds = (from dp in _discountRepository.Table
from c in dp.AppliedToCategories
select c).ToList().DistinctBy(d => d.Id).ToList();
var discount_AppliedToManufacturerIds = (from dp in _discountRepository.Table
from m in dp.AppliedToManufacturers
select m).ToList().DistinctBy(d => d.Id).ToList();
var product_Manufacturer_Mapping = (from dp in productlist
from pm in dp.ProductManufacturers
select pm).ToList().DistinctBy(d => d.Id).ToList();
var product_Category_Mapping = (from dp in productlist
from pc in dp.ProductCategories
select pc).ToList().DistinctBy(d => d.Id).ToList();
var ss = (from p in productlist
join dap in discount_AppliedToProductIds on p.Id equals dap.Id
join pcm in product_Category_Mapping on p.Id equals pcm.ProductId
//join dac in discount_AppliedToCategorieIds on pcm.CategoryId equals dac.Id
from dac in discount_AppliedToCategorieIds
join pmm in product_Manufacturer_Mapping on p.Id equals pmm.ProductId
join dam in discount_AppliedToManufacturerIds on pmm.ManufacturerId equals dam.Id
from dapd in dap.AppliedDiscounts
from pacd in dac.AppliedDiscounts
from damd in dam.AppliedDiscounts
where discountIds.Any(d => dapd.Id == d || d == pacd.Id || d == damd.Id)
// innner join condition
where categoryIds.Any(d => d == dac.Id) && dac.Id == pcm.CategoryId
select p).ToList();
I have write this code into c# but this code not provide proper result. Now I don't know what is problem into this code. If I run this code into sql server, then I get proper result, but in c# code I don't get proper result.
It's been a long time since I wrote a query in LINQ, but I seem to recall that if you wish to model a LEFT JOIN, you have to use DefaultIfEmpty(), otherwise you end up with an INNER JOIN.
See this, it's answer shows where to apply DefaultIfEmpty:
Linq join iquery, how to use defaultifempty
Obviously if you don't correctly model a LEFT JOIN expression, you'll end up with results only when all 3 inputs produce values.
I would also suggest not using .ToList() on each of your source queries, because that's going to manifest data into memory and use LINQ to Objects for your final query. If you remove the .ToList(), LINQ will construct a single database query for the entire process.
Mark

Left Join If A Certain Field Is A Certain Value?

I'm having a difficult time building a left join query that does something like this:
var Results =
from a in Db.Table1
join b in Db.Table2 on a.Id equals b.Id into c //IF b.Value2 == 1
from d in c.DefaultIfEmpty()
select new {Table1 = a};
The problem I'm having is that I simply can't add "where b.Value2==1" before the "select" because this will exclude all joined rows with b.Value2 != 1.
What I want to have happen is for the columns to join only if b.Value2==1, and if not, it will keep the row (instead of excluding them as the where described above would), but just not join the two columns.
I hope this makes sense and there is a way to do this, thanks!
Edit:
Table1
ValueA=1,ValueB=1
ValueA=2,ValueB=2
Table2
ValueB=1,ValueC=1
ValueB=2,ValueC=2
Desired Result (joining on Table1.ValueB==Table2.ValueB Where Table2.ValueC == 2, still include this row, but just don't join it to Table2):
ValueA=1, ValueB=1, ValueC=null //ValueC=null because where Table2.ValueB==1,ValueC != 2
ValueA=2, ValueB=2, ValueC=2
This should work, basically create a dummy var and join on it. The problem is this doesn't make for a friendly push down operation to the DB.
var Results =
from a in Db.Table1
join b in Db.Table2 on
new { ID= a.Id, Bit = true }
equals
new { ID = b.Id, Bit = b.Value ==1 }
into c
from d in c.DefaultIfEmpty()
select new {
Table1 = a,
Table2= d //will be null if b.Value is not 1
};
add the where, then add another query that's just for b.Value2 != 1, merge both queries?
Create two linq queries, which will be composed into one sql query:
The first query will do a regular outer join
var partResult= from a in Db.Table1
join b in Db.Table2 on a.Id equals b.Id into c
from d in c.DefaultIfEmpty()
select new { Table1 = a, Table2=d};
The second query will select all entries from the first query where Table2 is null or Table2.Id==1
var result = from obj in partResult
where (obj.Table2 == null || obj.Table2.Id == 2)
select obj.Table1;
This will be composed into a single select statement when the queries execute:
SELECT [t0].[Id]
FROM [dbo].[Table1] AS [t0]
LEFT OUTER JOIN (
SELECT 1 AS [test], [t1].[Id]
FROM [dbo].[Table2] AS [t1]
) AS [t2] ON [t0].[Id] = [t2].[Id]
WHERE ([t2].[test] IS NULL) OR ([t2].[Id] = 2)
if Table 1 has Ids 1, 2, 3, 4, 5, 6 and Table2 has Ids 1, 2, 3, 4 the result will be 1,5,6

SQL to NHibernate Criteria Query

I'm using Sharp Architecture for an MVC application and I'm trying to generate a nhibernate criterion query for the following SQL:
select top 10 p.* from Tab1 p
join Tab2 v on p.Id = v.Tab1Fk
join Tab3 sbu on v.Id = sbu.Tab2Fk
where sbu.DateTime >= 12/12/2002 00:00:00
group by p.Id, p.Name, p.CoCode, p.CuCode, p.StCode, p.ParentFk, p.LastGenerated
order by COUNT(sbu.Id) desc
Currently I've got:
var crit = Session.CreateCriteria<Tab1>();
crit.SetMaxResults(numberOfRecords);
crit.CreateCriteria("Tab2", "v", JoinType.InnerJoin)
.CreateCriteria("Tab3", "sbu", JoinType.InnerJoin)
.Add(Restrictions.Ge("sbu.DateTime", since))
.AddOrder(Order.Desc(Projections.Count("sbu.Id")));
return crit.List<Tab1>();
But it throws errors, can anyone help?
Have you considered using either HQL or NHibernate.Linq? I have several projects under my belt, and I almost always never even bother with the criteria api.
const string hql = #"select p.*
from Tab1 p
join p.Tab2 v where p.Tab2.Id = v.Id
join p.Tab3 sbu where v.Id = sbu.Tab2.Id
and sbu.DateTime >= :since
and rownum <= 10
group by p.Id, p.Name, p.CoCode
order by count(sbu.Id) desc";
var list = Session.CreateQuery(hql)
.SetParameter("since", DateTime.Now /* your date */)
.List<Tab1>();
It seems like you're good with SQL, so either of them would seem natural.
Edit: I must say I have never ordered by count(item), so my syntax there might be off. :)

Categories

Resources