I need help with doing a Left join in a linq statement. My T-sql query works as expected but I can't seem to get the wanted results from the Linq. I also realize that there are ton of questions like mine, but I can't seem to apply any of the solutions to my case.
Products table
+---+------------+-----------+
| |transportID | Type(int)|
+---+------------+-----------+
| 1 | 5 | 1 |
| 2 | 5 | 3 |
| 3 | 6 | 3 |
+---+------------+-----------+
Stores
+---+------------+-------------+
| |Name |Type1(string)|
+---+------------+-------------+
| 1 | Ho | 1 |
| 2 | He | 2 |
| 3 | Be | 3 |
| 4 | Ke | 4 |
| 5 | Fe | 5 |
+---+------------+-------------+
My wanted result is
+---+------------+-------------+
| |Type |Count |
+---+------------+-------------+
| 1 | 1 | 1 |
| 2 | 2 | 0 |
| 3 | 3 | 1 |
| 4 | 4 | 0 |
| 5 | 5 | 0 |
+---+------------+-------------+
My tsql that works as intended
SELECT
Type1,
Count(Pro.transportId) as Count
FROM dbo.stores as sto
left Join dbo.products as pro on (sto.Type1 = pro.Type AND pro.transportId=5)
Where Type1 is not null
group by Type1
ORDER BY Type1 * 1 ASC
My Linq attempt returns this.
+---+------------+-------------+
| |Type |Count |
+---+------------+-------------+
| 1 | 1 | 1 |
| 3 | 3 | 1 |
+---+------------+-------------+
Linq Statement.
var res = (from sto in _context.Stores
join pro in _context.Products on sto.Type1 equals System.Data.Objects.SqlClient.SqlFunctions.StringConvert((double)pro.Type).Trim()
where pro.transportId == transportId
group pro by pro.Type1 into pt1
select new TypeTransportation()
{
Type = pt1.Key, // Needs to be int
Count = pt1.Count()
}).ToList();
I've tried doing some defaultifempty but can't seem to make it work.
Here is MSDN link "How to: Perform Left Outer Joins" with LINQ: https://msdn.microsoft.com/en-gb/library/bb397895.aspx
You code should be like this:
var res = (from sto in _context.Stores
join pro in _context.Products on sto.Type1 equals System.Data.Objects.SqlClient.SqlFunctions.StringConvert((double)pro.Type).Trim() into grpJoin
from product in grpJoin.DefaultIfEmpty()
where product.transportId == transportId
group product by product.Type1 into pt1
select new TypeTransportation()
{
Type = pt1.Key, // Needs to be int
Count = pt1.Count()
}).ToList();
Wow .. lastly i did it ..
var transportId = 5;
var res = from s in _context.Stores
let Type = _context.Stores.Take(1).Select(x => s.Type1).Cast<int>().FirstOrDefault()
group Type by Type into pt1
select new TypeTransportation
{
Type = pt1.Key, // Needs to be int
Count = _context.Products.Where(i => i.transportId == transportId && i.Type == pt1.Key).Count()
};
foreach (var item in res)
{
Console.WriteLine(item.Type + " " + item.Count);
}
Console.ReadKey();
I can't do it in query syntax, but using extension method syntax it will be
var products = new[]
{
new {transportId = 5, Type = 1},
new {transportId = 5, Type = 3},
new {transportId = 6, Type = 3},
new {transportId = 5, Type = 3},
new {transportId = 5, Type = 5},
};
var stores = new[]
{
new {Name = "Ho", Type1 = "1"},
new {Name = "He", Type1 = "2"},
new {Name = "Be", Type1 = "3"},
new {Name = "Ke", Type1 = "4"},
new {Name = "Fe", Type1 = "5"},
};
var transportId = 5;
var res = stores
.GroupJoin(
inner: products
.Where(product =>
product.transportId == transportId),
innerKeySelector: product => product.Type,
outerKeySelector: store => Int32.Parse(store.Type1),
resultSelector: (store, storeProducts) =>
new
{
StoreType = store.Type1,
StoreName = store.Name,
ProductsCount = storeProducts.Count()
})
.ToList();
foreach (var item in res)
{
Console.WriteLine(item);
}
Just replace Int32.Parse with appropriate sql function call for actual DbContext query code.
With query syntax this is probably the best I can propose:
var res =
from store in stores
join product in
(from prod in products where prod.transportId == transportId select prod)
on store.Type1 equals product.Type.ToString() into storeProducts
select new
{
StoreType = store.Type1,
StoreName = store.Name,
ProductsCount = storeProducts.Count()
};
Basically you need to follow the left join pattern described in join clause (C# Reference). The only tricky part is the pro.transportId=5 condition in
left Join dbo.products as pro on (sto.Type1 = pro.Type AND pro.transportId=5)
The important thing is to not include it as where clause after the join.
One possible way to handle it is like this:
var res = (from sto in _context.Stores
join pro in _context.Products
on new { sto.Type1, transportId } equals
new { Type1 = pro.Type.ToString(), pro.transportId }
into storeProducts
from pro in storeProducts.DefaultIfEmpty()
group sto by sto.Type1 into pt
select new
{
Type = pt.Key, // the string value, there is no way to convert it to int inside the SQL
Count = pt.Count()
}).AsEnumerable() // switch to LINQ to Objects context
.Select(pt => new TypeTransportation()
{
Type = Convert.ToInt32(pt.Type), // do the conversion here
Count = pt.Count()
}).ToList();
or just apply it as where clause before the join:
var res = (from sto in _context.Stores
join pro in _context.Products.Where(p => p.transportId == transportId)
on sto.Type1 equals pro.Type.ToString()
into storeProducts
// the rest ... (same as in the first query)
Another detail to mention is that in order to make LEFT JOIN effectively apply, you need to group by the left table (Stores in your case) field (like in the original SQL query), thus ending up with a string key. If you wish to get the int key, there is no way to do it inside the db query, so you need to use a temporary projection, context switch and the final projection as shown above.
UPDATE: The last thing that I didn't realize initially is that the original SQL Count(Pro.transportId) is excluding NULLs from the right side of the join. So the final correct equivalent LINQ query is:
var res = (from sto in _context.Stores
join pro in _context.MyProducts
on new { sto.Type1, transportId } equals
new { Type1 = pro.Type.ToString(), pro.transportId }
into storeProducts
from pro in storeProducts.DefaultIfEmpty()
group new { sto, pro } by sto.Type1 into pt
select new
{
Type = pt.Key,
Count = pt.Sum(e => e.pro != null ? 1 : 0)
})
.AsEnumerable()
.Select(pt => new TypeTransportation()
{
Type = Convert.ToInt32(pt.Type),
Count = pt.Count
}).ToList();
Related
The first query :
Id | UserId | projectId |date | Status
1 | 1 | 1 | 2020 | PENDDING
2 | 1 | 2 | 2020 | DONE
3 | 2 | 1 | 2020 | PENDDING
And what I tried two queries :
the first is about to get all userwork with X project for example id = 1
var FirstQery = context.table1.where (C => C.ProjectId == 1).count();
The second query is to fetch the number of user with project x have "done"status
var SecondQery = context.table1.where (C => C.ProjectId == 1 && C.Status == "DONE").count();
I want return object have only two values : countNumberUserWithXProject
and countNumberUserByXProjectHaveXStatus
It is known approach with fake grouping.
var query =
from t in context.table1
where t.ProjectId == 1
group t by 1 into g
select new
{
Count = g.Count(),
DoneCount = g.Sum(x => x.Status == "DONE" ? 1 : 0)
}
var result = query.FirstOrDefault();
How about returning an anonymous object (or if you like it more a typized object like a int[]).
return new {count1 = FirstQery , count2 = SecondQery };
(Return, or assign like var result = new {count1....} etc
You can also replace the FirstQuery and SecondQuery directly with the Linq query.
i have two tables suck as the one below i wanna know how to sum "calorie" column based on name from table 1 and then insert the value to table 2
table1(PK->ID(int),Name(nvarchar),amount(int),calorie(int))
table2(pk->ID(int),name(nvarchar),totalcalorie(int))
+-------+--------+----------+--------------+
| int | name | amount | calorie |
+-------+--------+----------+--------------+
| 1 | a | 10 | 20 |
| 2 | b | 5 | 20 |
| 2 | b | 10 | 10 |
| 1 | a | 10 | 10 |
| 2 | b | 15 | 35 |
| 3 | c | 20 | 15 |
+-------+--------+----------+--------------+
something like this is my first table now imagine same kinda table for table2
only this time something like :
1-------a--------30
2-------b--------65
3-------c--------15
is this possible at all? what i wrote till now and doesn't work is this :
DataClasses1DataContext db = new DataClasses1DataContext();
var q = from row in db.table1
group row by new { row.name }
into grp
select new
{
grp.Key.name,
sum = grp.Sum(row => row.calorie)
};
db.SubmitChanges();
Now you are just selecting data. You need the block for insertion like the following:
var q = (from row in db.table1
group row by new { row.id, row.name } into grp
select new
{
grp.Key.id,
grp.Key.name,
sum = grp.Sum(s => s.calorie)
}).ToList();
foreach(var item in q)
{
var e = new db.table2Entity
{
id = item.id,
name = item.name,
totalcalorie = item.sum
};
db.Table2.AddObject(e);
}
db.SaveChanges();
I think that you do not see the result on you databse after db.SubmitChanges() wright ? Linq query should work fine but Submit doesn't see the change becouse there is no change in table2. You only select data and group it from table1. Please debug and see what is in q variable.
I want to grouping phone number to the same section like this :
section | phone
1 | 1001
1 | 1002
2 | 1201
2 | 1202
and grouping them in like this :
section | phone
1 | 1001, 1002
2 | 1201, 1202
but i don't know syntax to groping them to that.
This code i do
var entgr = (from fx in MainOnline.MA_TelUsers
where fx.TE_SectionID != null
group fx by fx.TE_SectionID into id
from ids in id.DefaultIfEmpty()
select new
{
Section = ids.TE_SectionID,
TelPhone = ids.TE_Phone
});
How I grouping that and use it to join other table?
var entgr = (from fx in ph
group fx by fx.SectionId.ToString() into id
select new
{
Section = id.Key,
TelPhone = string.Join(", ",id.Select(s => s.PhoneNumber.ToString()))
});
try this query
var entgr = (from fx in MainOnline.MA_TelUsers
where fx.TE_SectionID != null
group fx by fx.TE_SectionID into ids
select new
{
Section = ids.TE_SectionID,
TelPhone =ids.Aggregate((a, b) =>
new {TelPhone = (a.TelPhone + ", " + b.TelPhone ) }).TelPhone
});
https://msdn.microsoft.com/en-us/library/vstudio/bb397696.aspx
See this link. If you want to it do it in single linq query then that I hope not possible.
But at evaluation time you can do like this
var ph = new List<Phone>();
ph.Add(new Phone() { SectionId = 1, PhoneNumber = 1001 });
ph.Add(new Phone() { SectionId = 1, PhoneNumber = 1002 });
ph.Add(new Phone() { SectionId = 2, PhoneNumber = 1201 });
ph.Add(new Phone() { SectionId = 2, PhoneNumber = 1202 });
var results = ph.GroupBy(i => i.SectionId).Select(i=> new {i.Key, i});
foreach (var phone in results)
{
int section = phone.Key;
string phoneNos = string.Join(",",phone.i.Select(i=>i.PhoneNumber));
}
i want to join different column where the output would be this using linq
| Name | Address | Cellphone | Email |
| John | NY | n/a | johndoe#y.c |
| John | NY | 123456781 | n/a |
And i want my output to be one liner combined
| Name | Address | Cellphone | Email |
| John | NY | 123456781 | johndoe#y.c |
I Tried it on SQL server and this is the Query that answers the needed output
select a.ID, a.Name , a.Address ,(
SELECT wc1.[Values]
FROM Contact as wc1 where wc1.infoID = a.ID and wc1.ContactTypeID = 56) as Email
,(
SELECT wc1.[Values]
FROM Contact as wc1 where wc1.infoID = a.ID and wc1.ContactTypeID = 59) as Cellphone
from Info as a where a.ID = 100
Also tried it on Linq but it produces different row with same ID
var an = (from a in db.Info
join b in db.Contact on a.ID equals b.InfoID
where b.ContactTypeID == 56
|| b.ContactTypeID == 59
select new
{
a.ID,
a.LastName,
a.FirstName,
a.MiddleName,
b.ContactTypeID,
b.Values
}).ToList();
List<InfoList> wlist = new List<InfoList>();
foreach (var row in an)
{
InfoList ci = new InfoList
{
ID = row.ID,
Name = row.FirstName + " " + row.MiddleName + " " + row.LastName,
ContactType = GetLookupDisplayValById(row.ContactTypeID),
ContactValue = row.Values
};
wlist.Add(ci);
}
return Json(wlist.ToList(), JsonRequestBehavior.AllowGet);
Can someone help me translate this to a Linq Statement
Your SQL does not use JOIN, so why are you trying to introduce it in LINQ?
var an = (from a in db.Info
select new
{
a.ID,
a.LastName,
a.FirstName,
a.MiddleName,
Email = db.Contact.FirstOrDefault(b => b.InfoID == a.ID && b.ContactTypeIF == 56).Values,
Cellphone = db.Contact.FirstOrDefault(b => b.InfoID == a.ID && b.ContactTypeIF == 59).Values,
}).FirstOrDefault(x => x.ID == 100);
You can use the Pivot table the mentioned link shows how to use it: http://www.codeproject.com/Tips/500811/Simple-Way-To-Use-Pivot-In-SQL-Query
I have a property database and I am trying to get all properties added by an user. The main table is called 'Property' and there are other tables which are 'PropertyPhotos', 'City' etc. A sample database is as follows:
'Property' table
PropertyId| Area| State| UserId | ...
1 | 1 | 1 | AAA | ...
2 | 2 | 3 | BBB | ...
3 | 1 | 1 | AAA | ...
'PropertyPhotos'
PropertyPhotoId| PropertyId| FileName | MainPic
1 | 1 | x1.jpg | 1
2 | 1 | X2.jpg | 0
3 | 2 | x3.jpg | 1
4 | 3 | x4.jpg | 1
5 | 3 | x5.jpg | 0
6 | 3 | x6.jpg | 0
'AreaLookUp'
AreaLookUpId | AreaDescription
1 | London
2 | Birmingham
3 | Manchester
I am trying to write a LINQ query to get information on property added by a particular user. But I am stuck when trying to retrieve the 'FileName' of the MainPic and also get count. See code below with comments.
So, for the data above, this query should return the following for "UserId = AAA"
PropertyId | ... | MainPicSrc | PhotoCount
1 | ... | x1.jpg | 2
3 | ... | xr4jpg | 3
Please help!
public IEnumerable<PropertyExcerptViewModel> GetAddedPropertyVmByUserId(string userId)
{
var addedProperties = from p in db.Property where p.UserId == userId
join pp in db.PropertyPhotos on p.PropertyId equals pp.PropertyId
join a in db.AreaLookUp on p.Area equals a.AreaLookUpId
select new PropertyExcerptViewModel
{
PropertyId = p.PropertyId,
PropertyType = p.PropertyType,
TransactionType = p.TransactionType,
IsPropertyDisabled = p.IsPropertyDisabled,
IsPropertyVerified = p.IsPropertyVerified,
IsPropertyNotified = p.IsPropertyNotified,
MainPicSrc = pp.FileName, // How to put where condition to only get FileName of just the Main Pic
PhotoCount = pp.Count(), // How to get count of all pics with a particular proprtyId
Price = p.Price,
NoOfBedrooms = p.NoOfBedrooms,
Area = a.AreaLookUpDescription,
ShortDescription = (p.Description.Length > 300) ? p.Description.Substring(0,300) : p.Description
};
return addedProperties.ToList();
}
I think where statement might be easier if you care about clear match
var data=(from c in db.Property from v in db.PropertyPhotos from
n in db.AreaLookUpId
where c.PropertyId==v.PropertyId && c.Area==n.AreaLookUpId && c.UserId=="AAA"
// the rest is your select
PhotoCount = v.Where(j=>j. PropertyId==c.PropertyId).Count()
This also works - I ended up doing it this way
var addedProperties = from p in db.Property
join ppic in db.PropertyPhotos on p.PropertyId equals ppic.PropertyId into pp
join a in db.AreaLookUp on p.Area equals a.AreaLookUpId
join cal in db.CalendarEvent on p.PropertyId equals cal.PropertyId into c
where p.UserId == userId
select new PropertyExcerptViewModel
{
PropertyId = p.PropertyId,
PropertyType = p.PropertyType,
PropertyCategoryDescription = pc.PropertyCategoryDescription,
TransactionType = p.TransactionType,
IsPropertyDisabled = p.IsPropertyDisabled,
IsPropertyVerified = p.IsPropertyVerified,
IsPropertyNotified = p.IsPropertyNotified,
MainPicSrc = pp.Where(e => e.MainPic == true).FirstOrDefault().PhotoLocation,
PhotosCount = pp.Count(),
Price = p.Price,
NoOfBedrooms = p.NoOfBedrooms,
Area = a.AreaLookUpDescription,
ShortDescription = (p.Description.Length > 300) ? p.Description.Substring(0, 300) : p.Description,
LatestCalendarEvent = c.OrderByDescending(e => e.DateSaved).FirstOrDefault()
};
return addedProperties.ToList();