SQL Server efficient sub-total in EF Core - sql-server

I'm trying to achieve a query similar to this:
SELECT r.*, (SELECT COUNT(UserID) FROM RoleUsers ru WHERE ru.RoleId = r.Id) AS Assignments
FROM Roles r
To retrieve the number of the users per each role.
The simplest and the most straightforward option to implement desired output:
this.DbContext.Set<Role>().Include(x => x.RoleUser)
.Select(x => new { x, Assignments = x.RoleUsers.Count() });
Retrieves all the roles, and then N queries to retrieve count:
SELECT COUNT(*)
FROM [dbo].[RoleUsers] AS [r0]
WHERE #_outer_Id = [r0].[RoleId]
Which is not an option at all. I tried also to use GroupJoin, but it loads all the required data set in one query and performs grouping in memory:
this.DbContext.Set<Role>().GroupJoin(this.DbContext.Set<RoleUser>(), role => role.Id,
roleUser => roleUser.RoleId, (role, roleUser) => new
{
Role = role,
Assignments = roleUser.Count()
});
Generated query:
SELECT [role].[Id], [role].[CustomerId], [role].[CreateDate], [role].[Description], [role].[Mask], [role].[ModifyDate], [role].[Name], [assignment].[UserId], [assignment].[CustomerId], [assignment].[RoleId]
FROM [dbo].[Roles] AS [role]
LEFT JOIN [dbo].[RoleUser] AS [assignment] ON [role].[Id] = [assignment].[RoleId]
ORDER BY [role].[Id]
Also, I was looking into a way, to use windowing functions, where I can just split count by partition and use distinct roles, but I have no idea how to wire up windowing function in EF:
SELECT DISTINCT r.*, COUNT(ra.UserID) OVER(PARTITION BY ru.RoleId)
FROM RoleUsers ru
RIGHT JOIN Roles r ON r.Id = ru.RoleId
So, is there any way to avoid EntitySQL?

Currently there is a defect in EF Core query aggregate translation to SQL when the query projection contains a whole entity, like
.Select(role => new { Role = role, ...}
The only workaround I'm aware of is to project to new entity (at least this is supported by EF Core) like
var query = this.DbContext.Set<Role>()
.Select(role => new
{
Role = new Role { Id = role.Id, Name = role.Name, /* all other Role properies */ },
Assignments = role.RoleUsers.Count()
});
This translates to single SQL query. The drawback is that you have to manually project all entity properties.

this.DbContext.Set<Role>()
.Select(x => new { x, Assignments = x.RoleUsers.Count() });
you dont need to add include for RoleUser since you are using Select statement. Furhtermore, I guess that you are using LazyLoading where this is expected behavior. If you use eager loading the result of your LINQ will run in one query.
you can use context.Configuration.LazyLoadingEnabled = false; before your LINQ query to disable lazy loading specifically for this operation

Related

Write an aggregate function

Can we write an aggregate function for my SQL script.? As I expect that I can make a function as string_agg of SQL Server.
Select e.EmployeeId, MyAggregateFunction(e.Task, ', ')
From EmployeeTask as e
Group by e.EmployeeId
I know I can use string_agg. But I want to write it myself to use it in entity framework. Entity Framework cannot translate into string_agg.
This is my code in Entity Framework:
var result = _context.EmployeeTask.FindAsQueryable().GroupBy(x => x.EmployeeId).Select(x => new {EmployeeId = x.Key, Tasks = DbFunction.MyAggregateFunction(x => x.Task)});
I don't want to use .AsEnumrable() before GroupBy().

Need my SQL Query translated to Entity Framework code query

I need to able to translate my sql query to EF code, my sql query is using one where statement of IN operator and not sure how to also do that in EF.
I have tried doing a EF code the follwing code below but is not working.
private ManufacturingDbContext _manufacturingDbContext;
public List<string> GetManufacturerOrders()
{
var context = _manufacturingDbContext;
var ids = new[] {1, 2};
var manufacturingOrderList = context.ManufacturingOrders.Where(s => s.statusId == ids.Contains(s.statusId)).Select(o => o.lookupCode).ToList();
return manufacturingOrderList;
}
Here is the sql query where I need it translated to EF Code
select
o.lookupCode
from dbo.ManufacturingOrders o
where o.statusId in(1, 2)
the end result of this is to just get the lookupcode as you can see in my sql query, and that will display in my app. I looked other sites in google and also here and I could not find an exact answer of my question.
Should be something like:
var manufacturingOrderList = context.ManufacturingOrders
.Where(s => ids.Contains(s.statusId))
.Select(o => o.lookupCode)
.ToList();

Using left join instead of union in EF with includeProperties

When I include multiple properties in EF it generates 'UNION ALL' SQL queries to include them. If I have 3 properties to include, it is almost 3 times slower to run it.
If I create the same query with 3 left joins it is performing much better...
Is it possible to use left join instead of union when linq is generating the query?
If it is not possible what is the reason to use union all, which seems slow?
First of all, you can perform left joins in LINQ to entities using DefaultIfEmpty.
Second of all, if your navigation properties are 1:N (and not 1:1 or 1:0..1), then using 3 joins will severely increase the output volume from the database and this will reduce the performance, in this case, running one query to retrieve the main entities, then 3 additional queries based on the IDs from the first query, ran in a single DB call using the .Future() extension, will result in better performance.
E.g.
var entities = context.Entities.AsQueryable().Where(...).ToList();
var ids = entities.select(e => e.Id).ToList();
var subEntities1Query = context.SubEntities1.AsQueryable().Where(se1 =>
ids.Contains(se1.ParentId)).Future();
var subEntities2Query = context.SubEntities2.AsQueryable().Where(se2 =>
ids.Contains(se2.ParentId)).Future();
var subEntities3Query = context.SubEntities3.AsQueryable().Where(se3 =>
ids.Contains(se3.ParentId)).Future();
var subEntities1 = subEntities1Query.ToList();
var subEntities2 = subEntities2Query.ToList();
var subEntities3 = subEntities3Query.ToList();
foreach (var entity in entities)
{
entity.SubEntities1 = subEntities1.Where(se1 =>
se1.ParentId = entity.Id).ToList();
entity.SubEntities2 = subEntities2.Where(se2 =>
se2.ParentId = entity.Id).ToList();
entity.SubEntities3 = subEntities3.Where(se3 =>
se3.ParentId = entity.Id).ToList();
}

Doctrine Query Builder not working with UPDATE and INNER JOIN

In my repository I have this query:
$qb = $this->getEntityManager()->createQueryBuilder();
$qb
->update('MyBundle:Entity1', 'e1')
->join('e1.Entity2', 'e2')
->set('e1.visibile', '1')
->andWhere('e2.id = :id')->setParameter("id", 123)
;
throw this error
[Semantical Error] line 0, col 66 near 'e2.id = :id': Error: 'e2' is not defined
I have checked the relation and it is right.
Is there any issue using join in query update?
You can not use join on update and delete queries. You have to use subqueries.
Joins are not supported on update and delete queries because it is not
supported on all dbms. It won't be implemented in Doctrine 1 or
Doctrine 2. You can however get the same affect by using subqueries.
http://www.doctrine-project.org/jira/browse/DC-646
If you are using MySQL, using subqueries will not work. You will have then to use 2 queries.
In MySQL, you cannot modify a table and select from the same table in
a subquery
http://dev.mysql.com/doc/refman/5.0/en/subqueries.html
Doctrine DQL does not support join in update.
Try doing the following :
$qb = $this->getEntityManager()->createQueryBuilder();
$qb
->update('MyBundle:Entity1', 'e1')
->set('e1.visibile', '1')
->where('e1.Entity2 = :id')
->setParameter("id", 123)
;
You can set the id, as long as it is the primary key, of the linked entity directly as if it was the entity, Doctrine will map it.
I'm doing the exact same thing in my queries and it works.
try using a subquery instead Join will not work in DQL while you re doing an update:
LEFT JOIN, or JOINs in particular are only supported in UPDATE
statements of MySQL. DQL abstracts a subset of common ansi sql, so
this is not possible. Try with a subselect:
$qb = $this->getEntityManager()->createQueryBuilder();
$qb ->update('MyBundle:Entity1', 'e')
->set('e.visibile', '1')
->where('e.id IN (SELECT e1.id FROM Entity1 e1 INNER JOIN e2.Entity2 e2 WHERE e2 = :id')
->setParameter("id", 123);
Very old question, but do not contain an answer in full query builder.
So yes, the following query is not possible to sync fields of two tables:
$this->createQueryBuilder('v')
->update()
->join(Pegass::class, 'p', Join::WITH, 'v.identifier = p.identifier')
->set('v.enabled', 'p.enabled')
->where('p.type = :type')
->setParameter('type', Pegass::TYPE_VOLUNTEER)
->andWhere('v.enabled <> p.enabled');
The generated query do not contain the relation because of its lack of support in all dbms as explained above. They also tell you to use subqueries instead.
So that's how I did the equivalent (even if using 2 queries and is less performant...):
foreach ([false, true] as $enabled) {
$qb = $this->createQueryBuilder('v');
$sub = $this->_em->createQueryBuilder()
->select('p.identifier')
->from(Pegass::class, 'p')
->where('p.type = :type')
->andWhere('p.enabled = :enabled');
$qb
->setParameter('type', Pegass::TYPE_VOLUNTEER)
->setParameter('enabled', $enabled);
$qb
->update()
->set('v.enabled', $enabled)
->where($qb->expr()->in('v.identifier', $sub->getDQL()))
->getQuery()
->execute();
}

Convert this SQL Query to LINQ Query

I need to convert this SQL Query to LINQ Query, also I need to expose the SQL Select properties:
SELECT Problem.ProblemID, ProblemFactory.ObjectiveID, Objective.Name, ProblemFactory.Time, ProblemType.ProblemTypeName, ProblemFactory.OperationID,
ProblemFactory.Range1ID, ProblemFactory.Range2ID, ProblemFactory.Range3ID, ProblemFactory.Range4ID,
ProblemFactory.MissingNumber
FROM Problem INNER JOIN ProblemFactory ON Problem.ProblemFactoryID = ProblemFactory.ProblemFactoryID
INNER JOIN ProblemType ON ProblemFactory.ProblemTypeID = ProblemType.ProblemTypeID
INNER JOIN Objective ON Objective.ObjectiveID = ProblemFactory.ObjectiveID
UPDATE 1:
This is what I have:
var query = from problem in dc.Problem2s
from factory
in dc.ProblemFactories
.Where(v => v.ProblemFactoryID == problem.ProblemFactoryID)
.DefaultIfEmpty()
from ...
And I'm using this example: What is the syntax for an inner join in LINQ to SQL?
Something like this?
var query =
from p in ctx.Problem
join pf in ctx.ProblemFactory on p.ProblemFactoryID equals pf.ProblemFactoryID
join pt in ctx.ProblemType on pf.ProblemTypeID equals pt.ProblemTypeID
join o in ctx.Objective on pf.ObjectiveID equals o.ObjectiveID
select new
{
p.ProblemID,
pf.ObjectiveID,
o.Name,
pf.Time,
pt.ProblemTypeName,
pf.OperationID,
pf.Range1ID,
pf.Range2ID,
pf.Range3ID,
pf.Range4ID,
pf.MissingNumber,
};
But what do you mean by the "SQL Select properties"?
One of the benefits of an ORM like Linq-to-SQL is that we don't have to flatten our data to retrieve it from the database. If you map your objects in the designer (i.e. if you have their relationships mapped), you should be able to retrieve just the Problems and then get their associated properties as required...
var problems = from problem in dc.Problem2s select problem;
foreach (var problem in problems)
{
// you can work with the problem, its objective, and its problem type.
problem.DoThings();
var objective = problem.Objective;
var type = problem.ProblemType;
}
Thus you retain a logical data structure in your data layer, rather than anonymous types that can't easily be passed around.

Resources