Doctrine Query Builder not working with UPDATE and INNER JOIN - database

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();
}

Related

How to use AND , OR , () clauses in Solr query?

I need a Solr query with AND, (), OR, clauses, something like this
search +TYPE:"ecmcndgostst:nd_gost_standards" +#ecmcnddoc\:doc_kind_cp_ecmcdict_value:"gost_gost" AND (+#ecmcnddoc\:biblio_fond:"standard" OR +#ecmcnddoc\:biblio_fond:"regulations")
(it's just an example this query doesn't work).
It is an attempt to re-write in Solr the following CMIS query:
SELECT p.cmis:objectId FROM ecmcnddoc:common_attr_aspect AS d JOIN ecmcnddoc:biblio_attr_aspect AS p ON d.cmis:objectId = p.cmis:objectId JOIN ecmcnddoc:reg_attr_aspect AS s ON s.cmis:objectId = p.cmis:objectId JOIN ecmcnddoc:spec_attr_aspect AS asp ON asp.cmis:objectId = p.cmis:objectId WHERE p.cmis:objectTypeId='D:ecmcndgostst:nd_gost_standards' AND d.ecmcnddoc:doc_kind_cp_ecmcdict_value='gost_gost' AND (p.ecmcnddoc:biblio_fond='standard' OR p.ecmcnddoc:biblio_fond='regulations')
Could you help me with Solr ?

SQL Server efficient sub-total in EF Core

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

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.

Choosing SQL join types for Entity Framework 4

I have a query for example:
var personList = context.People;
People is a view that has 2 joins on it and about 2500 rows and takes ~10 seconds to execute.
Looking at the Estimated Execution plan tells me that it is using a nested loop.
Now if i do this:
var personList = context.People.Where(r => r.Surname.Length > -1);
Execution time is under a second and the execution plan is using a Hash Join.
Adding "OPTION (HASH JOIN)" to the generated SQL has the desired effect of increasing performance.
So my question is ...
How can i get the query to use a Hash Join? It can't be added to the view (I tried, it errors).
Is there an option in EF4 that will force this? Or will i have to put it in a stored procedure?
RE: View
SELECT dbo.DecisionResults.ID, dbo.DecisionResults.UserID, dbo.DecisionResults.HasAgreed, dbo.DecisionResults.Comment,
dbo.DecisionResults.DateResponded, Person_1.Forename, Person_1.Surname, Site_1.Name, ISNULL(dbo.DecisionResults.StaffID, - 999)
AS StaffID
FROM dbo.DecisionResults INNER JOIN
Server2.DB2.dbo.Person AS Person_1 ON Person_1.StaffID = dbo.DecisionResults.StaffID INNER JOIN
Server2.DB2.dbo.Site AS Site_1 ON Person_1.SiteID = Site_1.SiteID
ORDER BY Person_1.Surname
If i add OPTION(HASH JOIN) to the end it will error with :
'Query hints' cannot be used in this query type.
But running that script as a query works fine.

How to do Sql Server CE table update from another table

I have this sql:
UPDATE JOBMAKE SET WIP_STATUS='10sched1'
WHERE JBT_TYPE IN (SELECT JBT_TYPE FROM JOBVISIT WHERE JVST_ID = 21)
AND JOB_NUMBER IN (SELECT JOB_NUMBER FROM JOBVISIT WHERE JVST_ID = 21)
It works until I turn it into a parameterised query:
UPDATE JOBMAKE SET WIP_STATUS='10sched1'
WHERE JBT_TYPE IN (SELECT JBT_TYPE FROM JOBVISIT WHERE JVST_ID = #jvst_id)
AND JOB_NUMBER IN (SELECT JOB_NUMBER FROM JOBVISIT WHERE JVST_ID = #jvst_id)
Duplicated parameter names are not allowed. [ Parameter name = #jvst_id ]
I tried this (which i think would work in SQL SERVER 2005 - although I haven't tried it):
UPDATE JOBMAKE
SET WIP_STATUS='10sched1'
FROM JOBMAKE JM,JOBVISIT JV
WHERE JM.JOB_NUMBER = JV.JOB_NUMBER
AND JM.JBT_TYPE = JV.JBT_TYPE
AND JV.JVST_ID = 21
There was an error parsing the query. [ Token line number = 3,Token line offset = 1,Token in error = FROM ]
So, I can write dynamic sql instead of using parameters, or I can pass in 2 parameters with the same value, but does someone know how to do this a better way?
Colin
Your second attempt doesn't work because, based on the Books On-Line entry for UPDATE, SQL CE does't allow a FROM clause in an update statement.
I don't have SQL Compact Edition to test it on, but this might work:
UPDATE JOBMAKE
SET WIP_STATUS = '10sched1'
WHERE EXISTS (SELECT 1
FROM JOBVISIT AS JV
WHERE JV.JBT_TYPE = JOBMAKE.JBT_TYPE
AND JV.JOB_NUMBER = JOBMAKE.JOB_NUMBER
AND JV.JVST_ID = #jvst_id
)
It may be that you can alias JOBMAKE as JM to make the query slightly shorter.
EDIT
I'm not 100% sure of the limitations of SQL CE as they relate to the question raised in the comments (how to update a value in JOBMAKE using a value from JOBVISIT). Attempting to refer to the contents of the EXISTS clause in the outer query is unsupported in any SQL dialect I've come across, but there is another method you can try. This is untested but may work, since it looks like SQL CE supports correlated subqueries:
UPDATE JOBMAKE
SET WIP_STATUS = (SELECT JV.RES_CODE
FROM JOBVISIT AS JV
WHERE JV.JBT_TYPE = JOBMAKE.JBT_TYPE
AND JV.JOB_NUMBER = JOBMAKE.JOB_NUMBER
AND JV.JVST_ID = 20
)
There is a limitation, however. This query will fail if more than one row in JOBVISIT is retuned for each row in JOBMAKE.
If this doesn't work (or you cannot straightforwardly limit the inner query to a single row per outer row), it would be possible to carry out a row-by-row update using a cursor.

Resources