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.
Related
I want to get last user updated with a linq lambda expression using group by and the count of the remaining users. I don't know how I can do that.
here is my data :
userid | name | datetime | isdelete
1 | abc | 16-03-2017 15:45:59 | 0
1 | abc | 16-03-2017 12:45:10 | 0
2 | xyz | 16-03-2017 15:45:59 | 0
1 | abc | 16-03-2017 10:40:59 | 0
I want the result to look like this:
userid | name | datetime | count
1 | abc | 16-03-2017 15:45:59 | 3
2 | xyz | 16-03-2017 15:45:59 | 1
Here the count for userid = 1 should be 3 as there are three records for that id in the table.
I have written this query, but it is getting all the records.
List<Users> UList = new List<Users>();
UList = db.Users.Where(a => a.isdelete == false)
.OrderByDescending(a => a.datetime)
.Skip(skip)
.Take(pageSize)
.ToList();
Anyone know how I can get the data I want? Please let me know using linq lambda expression.
You need to group by user, than sort each group and take first from each group
var UList = (db.Users
.Where(a => a.isdelete == false)
.GroupBy(a => a.UserId)
.Select(g => new MyNewClass
{
Count = g.Count(),
User = g.OrderByDescending(a => a.datetime).First()
}
))
.Skip(skip)
.Take(pageSize)
.ToList();
You forgot to group your data:
var result = db.Users.Where(a => !a.isdelete)
.GroupBy(x => x.userid)
.Select(x => new User
{
userid = x.Key,
name = x.Last().Name,
datetime = x.OrderByDescending(a => a.datetime).First().datetime,
count = x.Count()
});
EDIT: This might be not optimal considering the performance as the call to Last and OrderByAscending will both iterate the whole data. To overcome this a bit you may re-structure this query a bit:
var result = db.Users.Where(a => !a.isdelete)
.GroupBy(x => x.userid)
.Select(x => new
{
user = x.OrderByDescending(a => a.datetime).First(),
count = x.Count()
})
.Select(x => new User {
name = x.user.name,
userid = x.user.userid,
datetime = x.user.datetime,
count = x.count
});
I have a history table of ordered products
+----+------------+--------+
| id | IdProduct | status |
+----+------------+--------+
| 1 | 100 | 1 |
| 2 | 100 | 2 |
| 3 | 100 | 3 |
| | | |
| 4 | 200 | 1 |
| 5 | 200 | 2 |
| | | |
| 6 | 300 | 1 |
| 7 | 300 | 2 |
+----+------------+--------+
I want to get only the products who have the status 2 but not 3
+----+------------+
| id | IdProduct |
+----+------------+
| 5 | 200 |
| | |
| 7 | 300 |
+----+------------+
How can I achieve this using a Linq request
Using linq to sql you can do:
var result = history.GroupBy(item => item.IdProduct)
.Where(grp => grp.Any(item => item.Status == 2) &&
!grp.Any(item => item.Status == 3))
.Select(grp => new {
IdProduct = grp.Key,
Id = grp.Max(item => item.Id)
});
Or:
var result = history.GroupBy(item => item.IdProduct)
.Where(grp => grp.Any(item => item.Status == 2) &&
!grp.Any(item => item.Status == 3))
.Select(grp => grp.Where(item => ite.Status == 2).FirstOrDefault());
In your case both these should return the same because the max(id) correleted with the result you wanted
If you know each status exists only once then you can try the following. The idea is that status 3 items equal -1, 2 equals 1 and the rest 0. Only groups that have status 2 but not 3 will have the result of 1
var result = history.Select(item => new { Item = item, Valid = item.Status == 2 ? 1 : item.Status == 3 ? -1 : 0 })
.GroupBy(item => item.Item.IdProduct)
.Where(grp => grp.Sum(item => item.Valid) == 1)
.Select(item => item.Item);
I didnt read the last line in question How can I achieve this using a Linq request may be that reason for downvote..
Will keep this answer if someone is looking to solve it in SQL
Here is one way using Group by and Having clause
SELECT *
FROM yourtable
WHERE IdProduct IN (SELECT IdProduct
FROM Yourtable
GROUP BY IdProduct
HAVING Count(CASE WHEN status = 3 THEN 1 END) = 0
AND Count(CASE WHEN status = 2 THEN 1 END) > 0)
AND Status = 2
Count(CASE WHEN status = 3 THEN 1 END) = 0
This condition is to make sure status = 3 does not exist any row for each ID
Count(CASE WHEN status = 2 THEN 1 END) > 0
This condition is to make sure alteast one row with status = 2 for each ID
Declare #YourTable table (id int,IdProduct int,status int)
Insert Into #YourTable values
( 1 , 100 , 1 ),
( 2 , 100 , 2 ),
( 3 , 100 , 3 ),
( 4 , 200 , 1 ),
( 5 , 200 , 2 ),
( 6 , 300 , 1 ),
( 7 , 300 , 2 )
Select Id,IdProduct
From (
Select Id
,IdProduct
,MaxStatus = max(Status) over (Partition By IdProduct)
From #YourTable
Where Status in (2,3)
) A
Where MaxStatus = 2
Returns
Id IdProduct
5 200
7 300
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();
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 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();