Linq to Sql - Converting a join and sum from sql to linq - linq-to-sql

I have crawled over several of the various questions on Linq-to-SQL dealing with joins, counts, sums and etc.. But I am just having a difficult time grasping how to do the following simple SQL Statement...
SELECT
tblTechItems.ItemName,
SUM(tblTechInventory.EntityTypeUID) AS TotalOfItemByType
FROM
tblTechInventory
INNER JOIN
tblTechItems ON tblTechInventory.ItemUID = tblTechItems.ItemUID
GROUP BY
tblTechInventory.StatusUID,
tblTechItems.ItemName,
tblTechInventory.InventoryUID
HAVING
tblTechInventory.StatusUID = 26

Try this:
var query = from e in db.tblTechInventory
join f in db.tblTechItems on e.ItemUID equals f.ItemUID into x
from y in x
group e by new { e.StatusUID, y.ItemName, e.InventoryUID } into g
where e.StatusUID == 26
select new {
g.Key.ItemName,
TotalOfItemByType = g.Sum(e => e.EntityTypeUID)
};

I'll give it a shot...
var results = tblTechInventory
.Join(tblTechItems, i=> i.ItemUID, o => o.ItemUID, (i,o) => new {i.ItemName, o.EntityTypeUID, o.StatusUID, i.ItemName, o.InventoryUID})
.Where(o => o.StatusUID == 26)
.GroupBy(g => new {g.StatusUID, g.ItemName, g.InventoryUID}, (gr, items) => new {gr.Key.ItemName, items.Sum(i => i.EntityTypeUID)});

Related

How to query in EF core with OrderByDescending, Take, Select and FirstOrDefault

So I've got a table named Summaries, it looks like this
I need to get to sum the latest entries of TotalPieces based on CoveredDate and should be grouped by ServiceCode and queried by month
for example, ServiceCode 'A' has entries on 2020-01-01, 2020-01-02, 2020-01-03, 2020-01-31, 2020-02-01, 2020-02-28, 2020-02-29
and ServiceCode 'B' has entries on 2020-01-01, 2020-01-02, 2020-01-31, 2020-02-20, 2020-02-21,
i need to get the sum based on month, lastest entry on 'A' on January is on 2020-01-31, and 'B' has latest entry on 2020-01-31, I need to sum their 'TotalPieces', so I should get 25 + 25 = 50.
basically i need to do is
Get all the lastest entries based on CoveredDate and month/year
Sum the TotalPieces by ServiceCode
i got a working query, but this is just a workaround because i can't get it right on query.
int sum_totalpieces = 0;
foreach (var serviceCode in service_codes)
{
var totalpieces = _DbContext.ActiveSummaries.Where(acs =>
acs.CoveredDate.Date.Month == query_month
&& acs.CoveredDate.Date.Year == query_year
&& acs.service_codes == serviceCode
)
.OrderByDescending(obd => obd.CoveredDate)
.Take(1)
.Select(s => s.TotalPieces)
.ToList()
.FirstOrDefault();
sum_totalpieces += totalpieces;
}
the service_codes is just a List of string
If you guys could just get rid of the foreach block their and make it services_codes.Contains() on query, or another workaround to make the result faster that would be great. Thanks a lot.
This will do it, but I don't think it will translate to SQL and run at the server:
_DbContext.ActiveSummaries
.Where(b =>
b.CoveredDate >= new DateTime(2020,1,1) &&
b.CoveredDate < new DateTime(2020,2,1) &&
new [] { "A", "B" }.Contains(b.ServiceCode)
)
.GroupBy(g => g.ServiceCode)
.Sum(g => g.OrderByDescending(gb=> gb.CoveredDate).First().TotalPieces);
If you want to do it as a raw SQL for best performance it would look like:
SELECT SUM(totalpieces)
FROM
x
INNER JOIN
(
SELECT servicecode, MAX(covereddate) cd
FROM x
WHERE x.servicecode IN ('A','B') AND covereddate BETWEEN '2020-01-01' AND '2020-01-31'
)y ON x.servicecode=y.servicecode and x.covereddate = y.cd

withCount() - get difference of 2 counts

I have a table that has a column is_up_vote = true|false
I am trying to write a query using withCount() so it returns me the sum of true values and false values.
select COUNT(is_up_vote) from comment_votes WHERE is_up_vote = true returns 17
select COUNT(is_up_vote) from comment_votes WHERE is_up_vote = false returns 15
However I couldn't figure out how to get different of them, so the total returns 2.
What I tried is:
Model::withCount(['votes' => function($q) {
$q->selectRaw(
'(SUM (COUNT(is_up_vote) WHERE is_up_vote = true) - (COUNT(is_up_vote) WHERE is_up_vote = false) )'
);
}]);
But this returns 17+15 = 32
without SUM(), it's also returning 32.
$q->selectRaw(
'( (COUNT(is_up_vote) WHERE is_up_vote = true) - (COUNT(is_up_vote) WHERE is_up_vote = false) )'
);
What am I doing wrong?
Edit:
If I try one side, it ignores the where and still returns 32, so the where is not getting called (where it does in sql)
return $query->withCount(['votes' => function($q) {
$q->selectRaw('(COUNT(is_up_vote) WHERE is_up_vote = true)');
}]);
maunal sequel query returns 17:
select COUNT(is_up_vote) from comment_votes WHERE is_up_vote = true
Edit 2:
$query->withCount([
'votes as up_votes_count' => function($q) {
$q->where('is_up_vote', true);
},
'votes as down_votes_count' => function($q) {
$q->where('is_up_vote', false);
},
]);
The only thing that I could make work is this, but it'd need extra step for getting the total, so I didn't really like this approach. I'm sure someone more proficient with queries can come up with something with direct query.
withCount may not be able to handle this case. what about using addSelect to calculate the votes count.
return Model::addSelect(['votes_count' => Vote::selectRaw('( (COUNT(is_up_vote) WHERE is_up_vote = true) - (COUNT(is_up_vote) WHERE is_up_vote = false) )')
->whereColumn('parent_id', 'parents.id')
])->get();
I don't know whether the sub-query is gonna work. But you know the idea.
According to the instructions commented by #Jefferson Pessanha, just using Model::selectRaw('(abs(2 * sum(is_up_vote) - count(*)))')->get(); would be enough
create table comment_votes
(
is_up_vote tinyint(1) default 1 null
);
insert into comment_votes values (true),(true),(true),(true),(true),(true),(true),(true),(true),(true),(true),(true),(true),(true),(true),(true),(true);
insert into comment_votes values (false),(false),(false),(false),(false),(false),(false),(false),(false),(false),(false),(false),(false),(false),(false);
select COUNT(is_up_vote) from comment_votes WHERE is_up_vote = true;
returns 17
select COUNT(is_up_vote) from comment_votes WHERE is_up_vote = false;
returns 15
select (abs(2 * sum(is_up_vote) - count(*))) from comment_votes;
returns 2
Explain:
trueValues + falseValues = total
falseValues = total - trueValues
trueValues - falseValues = x
trueValues - (total - trueValues) = x
2 * trueValues - total = x
trueValues = sum(is_up_vote) [only trues stored as 1 are counted]
total = count(*)
Abs function is to convert the result to positive in case of negative
Isn't it correct?
Try this.
select
sum( if(is_up_vote= true, 1, 0) ) as uptrue,
sum( if(is_up_vote= false, 1, 0) ) as upfalse,
sum( if(is_up_vote= true, 1, -1) ) as updiff
from comment_votes
Edit: 1
Instead of checking if it is true or false, try this.
select
sum( if(is_up_vote, 1, 0) ) as uptrue,
sum( if(is_up_vote, 0, 1) ) as upfalse,
sum( if(is_up_vote, 1, -1) ) as updiff
from comment_votes

How to group by in Entity Framework Core with no repetitive group?

I want to perform a group by in Entity Framework core with no repetitive groups.
Lets suppose I have two columns
Column A Column B
1 1
2 1
2 1
4 5
5 4
If a group by is performed for two columns Entity framework core the result is pretty obvious.
Column A Column B
1 1
2 1
4 5
5 4
But i want to perform a group by which works both ways A->B and B->A hence the result would be
Column A Column B
1 1
2 1
5 4
Any idea how to do that in Entity Framework Core?
My original attempt was to use Union
var user = _context.Transactions
.Where(p => !p.IsDeleted && (p.ReceiverUserId == userId) &&
(p.SenderUserId != null))
.Include(p => p.SenderUser)
.GroupBy(p => p.SenderUserId)
.Select(p => new TempModel { Id = p.FirstOrDefault().SenderUser.Id, User = p.FirstOrDefault().SenderUser, CreatedDate = p.FirstOrDefault().CreatedDate });
var user2 = _context.Transactions
.Where(p => !p.IsDeleted && (p.SenderUserId == userId) &&
(p.ReceiverUserId != null))
.Include(p => p.ReceiverUser)
.GroupBy(p => p.ReceiverUserId)
.Select(p => new TempModel { Id = p.FirstOrDefault().ReceiverUser.Id, User = p.FirstOrDefault().ReceiverUser, CreatedDate = p.FirstOrDefault().CreatedDate});
var finalQuery = user.Union(user2);
var finalQuery2 = finalQuery.GroupBy(p => p.Id);
var finalQuery1 = finalQuery2.OrderByDescending(p => p.FirstOrDefault().CreatedDate);
finalQuery.GroupBy(p => p.Id); <- this line gives error
You should sort these columns by descending: 4-5 => 5-4; 5-4 => 5-4;
5-5 => 5-5 and then group by or distinc by them:
var answer = db.Table.Select(x => new
{
ColumnA = x.ColumnA > x.ColumnB ? x.ColumnA : x.ColumnB,
ColumnB = x.ColumnA > x.ColumnB ? x.ColumnB : x.ColumnA
}).Distinct().ToList();

How do I achieve this in C#

How do I aggregate conditionally in C#
Like in the following vb code:
Dim movement = From item In dbo.VIEWITEMMOVEMENTs
Where item.DATETRANSFERRED.Value.Date = Me.DateTimePicker1.Value.Date
Group By item.DATETRANSFERRED.Value.Date, item.ITEMDESCRIPTION
Into moved = Group, sold = Sum(item.QUANTITYSOLD), [IN] = Sum(If(item.QUANTITY < 0.0, 0.0, item.QUANTITY)), Out = Sum(If(item.QUANTITY > 0, 0, item.QUANTITY))
Select ITEMDESCRIPTION, sold, [IN], Out Order By ITEMDESCRIPTION Ascending
Me.DataGridView1.DataSource = movement
var result = (
from item in dbo.VIEWITEMMOVEMENTs
where item.DATETRANSFERRED.Value.Date == this.DateTimePicker1.Value.Date
group item by new { item.DATETRANSFERRED.Value.Date, item.ITEMDESCRIPTION }
into g
select new
{
g.Key.ITEMDESCRIPTION,
sold = g.Sum(x => x.QUANTITYSOLD),
IN = g.Sum(x => (x.QUANTITY <0 ? 0 : x.QUANTITY)),
Out = g.Sum(x => (x.QUANTITY > 0 ? 0 : x.QUANTITY))
}
).OrderBy(d => d.ITEMDESCRIPTION);
if you are trying to convert that snippet of code. you could use the converters like
VB to C#
Hope this helps.

EF 4.1 - code first - query no longer works

I moved from using an edmx file to code first. The following query is no longer working (it's basically a group by with limit 1) :
(from sc in dbContext.student_courses
where
sc.student_id == student.id
&& sc.course_id == course.id
&& sc.complete_flag == 1
group sc by new
{
sc.complete_flag,
sc.direct_to_test_flag,
sc.dept_purchase_flag,
sc.issue_ce_flag,
sc.unlimited_purchase_flag,
sc.survey_taken
}
into scgroup
select new StudentCourseDetails
{
completeVideo = scgroup.Max(x => x.complete_flag),
directToTest = scgroup.Max(x => x.direct_to_test_flag),
isDepartmentPurchase = scgroup.Max(x => x.dept_purchase_flag),
issueCE = scgroup.Max(x => x.issue_ce_flag),
isUnlimitedPurchase = scgroup.Max(x => x.unlimited_purchase_flag),
surveyTaken = scgroup.Max(x => x.survey_taken)
}).FirstOrDefault();
The resulting sql :
SELECT `Limit1`.`complete_flag`,
`Limit1`.`C1`,
`Limit1`.`C2`,
`Limit1`.`C3`,
`Limit1`.`C4`,
`Limit1`.`C5`,
`Limit1`.`C6`
FROM (SELECT `GroupBy1`.`A1` AS `C1`,
`GroupBy1`.`A2` AS `C2`,
`GroupBy1`.`A3` AS `C3`,
`GroupBy1`.`A4` AS `C4`,
`GroupBy1`.`A5` AS `C5`,
`GroupBy1`.`A6` AS `C6`,
`GroupBy1`.`K1` AS `complete_flag`
FROM (SELECT Max(`complete_flag`) AS `A1`,
Max(`direct_to_test_flag`) AS `A2`,
Max(`dept_purchase_flag`) AS `A3`,
Max(`issue_ce_flag`) AS `A4`,
Max(`unlimited_purchase_flag`) AS `A5`,
Max(`survey_taken`) AS `A6`
FROM `student_courses` AS `Extent1`
WHERE ((`Extent1`.`student_id` = 3885 /* #p__linq__0 */)
AND (`Extent1`.`course_id` = 606 /* #p__linq__1 */))
AND (1 = `Extent1`.`complete_flag`)
GROUP BY `Extent1`.`complete_flag`,
`Extent1`.`dept_purchase_flag`,
`Extent1`.`direct_to_test_flag`,
`Extent1`.`issue_ce_flag`,
`Extent1`.`survey_taken`,
`Extent1`.`unlimited_purchase_flag`) AS `GroupBy1`
LIMIT 1) AS `Limit1`
The problem is that when it adds the limit 1, it also adds an extra column :
GroupBy1.K1 AS complete_flag - which doesn't exist.
Any thoughts ?