Doing a WHERE .. IN subquery in Doctrine 2
This is how I would try it:
/** @var Doctrine\ORM\EntityManager $em */
$expr = $em->getExpressionBuilder();
$em->createQueryBuilder()
->select(array('DISTINCT i.id', 'i.name', 'o.name'))
->from('Item', 'i')
->join('i.order', 'o')
->where(
$expr->in(
'o.id',
$em->createQueryBuilder()
->select('o2.id')
->from('Order', 'o2')
->join('Item',
'i2',
\Doctrine\ORM\Query\Expr\Join::WITH,
$expr->andX(
$expr->eq('i2.order', 'o2'),
$expr->eq('i2.id', '?1')
)
)
->getDQL()
)
)
->andWhere($expr->neq('i.id', '?2'))
->orderBy('o.orderdate', 'DESC')
->setParameter(1, 5)
->setParameter(2, 5)
;
I didn't test this of course, and made some assumptions about your models. Possible problems:
- Limit: this has been somewhat of a problem in Doctrine 2, it seems query builder is not very good at accepting limits. Do take a look here, here and here.
- The IN clause is usually used with an array, but I think it will work with a subquery.
- You probably can use the same parameter ?1, instead of two parameters (because they're the same value), but I'm not sure.
Concluding, this may not work first time, but will surely put you on the right track. Do tell us the final 100% correct answer afterwards.
Doing a subquery inside a where with Doctrine 2 querybuilder
For people interested I finally found a solution. I did the query like this (if someone has another solution I am very interested):
public function getConversations()
{
$rawSql = "SELECT * FROM `message` A
WHERE A.id =
(SELECT B.id FROM `message` B
WHERE (A.user_id = B.user_id AND A.receiver_id = B.receiver_id) OR (A.user_id = B.receiver_id AND A.receiver_id = B.user_id)
ORDER BY creationdate DESC
LIMIT 1)
";
$stmt = $this->getEntityManager()->getConnection()->prepare($rawSql);
$stmt->execute([]);
return $stmt->fetchAll();
}
WHERE ... IN query with sub-query in Doctrine queryBuilder or equivalent
your query can be turned into something without a sub-query, but with a join instead, which should be equivalent (and should have the same runtime/complexity)
SELECT u
FROM user u
LEFT JOIN user am ON (am.id=u.accountManager)
WHERE am.roles LIKE '%ROLE_MANAGER%'
AND am.accountManager=:managerAdminId
AND u.roles LIKE '%ROLE_PUBLISHER%'
which can be translated into querybuilder accordingly (I have to assume, that you did not define your associations ... which I find disturbing, but you probably have your reasons):
return $this->createQueryBuilder('u')
->leftJoin('App\Entity\User', 'am', 'WITH', 'am.id=u.accountManager')
->andWhere('am.roles LIKE :role')
->setParameter('role', '%ROLE_MANAGER%')
->andWhere('am.accountManager = :managerAdminId')
->setParameter('managerAdminId', $managerAdminId)
->andWhere('u.roles LIKE :role2')
->setParameter('role2', '%ROLE_PUBLISHER%')
->getQuery()->getResult();
there is also the options of actually using sub-queries, but using sub-queries imho is always inconvenient - and ugly.
(you might have a look into writing just plain DQL queries, you might feel more at home ...?)
Doctrine subquery andwhere
For such cases, you can rely on the Expr
class which contains the methods you need to build those more complex queries. Everything you need to know is explained in the Doctrine documentation in the Query Builder section.
The current documentation actually gives an example which almost fits with the use case you mentioned. You could write something like this for example:
$qb->andWhere($qb->expr()->orX(
$qb->expr()->eq('u.id', '?1'),
$qb->expr()->like('u.nickname', '?2')
));
The main advantage of relying on the Expr
class is that you can pretty much write any kind of combination with it and it will produce the right DQL for it. It can get pretty verbose however.
That being said, you can also directly write your OR condition in the andWhere
:
$qb->andWhere('u.id = ?1 OR u.nickname = ?2');
Doctrine DQL - nested subqueries in WHERE clause throw Syntax Error
Correct, the DBAL does not support arithmetic operations on subqueries. Additional the DBAL does not support joined subqueries, JOIN (SELECT FROM...) ON
without using a native query.
Another issue is the WHERE
statement of your subqueries, being dependant on the root query, will cause your root query to perform a full table scan. Executing each of the subqueries per-row, unless a criteria is added to the root query (WHERE o.id ...
).
As the subquery SUM
values are dependant on the root query id. You can rewrite the query to use a subquery as a hidden column and a HAVING
clause, to filter the id result set from the added column results.
$em = $queryBuilder->getEntityManager();
$expr = $em->getExpressionBuilder();
$qbPC = $em->createQueryBuilder()
->select('COALESCE(SUM(pc.residentialUnits), 0)')
->from(App\Entity\PropertyConnection::class, 'pc')
->where($expr->eq('pc.contract', "$rootAlias.id"));
$qbPWA = $em->createQueryBuilder()
->select('COALESCE(SUM(pwa.residential_units), 0)')
->from(App\Entity\PropertyWithoutAddress::class, 'pwa')
->where($expr->eq('pwa.contract', "$rootAlias.id"));
$qb = $this->getEntityManager(Contract::class)
->createQueryBuilder()
->select('o.id')
->from(App\Entity\Contract::class, 'o')
->addSelect('(' . $qbPC->getDQL() . ') AS HIDDEN pc_ru')
->addSelect('(' . $qbPWA->getDQL() . ') AS HIDDEN pwa_ru')
->having($expr->lte(':v', 'pc_ru + pwa_ru'))
->setParameter('v', $params['rangeFrom']);
dump($qb->getDQL());
Resulting DQL
SELECT
o.id,
(SELECT
COALESCE(SUM(pc.residentialUnits), 0)
FROM App\Entity\PropertyConnection pc
WHERE pc.contract = o.id
) AS HIDDEN pc_ru,
(SELECT
COALESCE(SUM(pwa.residential_units), 0)
FROM App\Entity\PropertyWithoutAddress pwa
WHERE pwa.contract = o.id
) AS HIDDEN pwa_ru
FROM App\Entity\Contract o
HAVING :v <= pc_ru + pwa_ru
Related Topics
How to Create Multiple One to One's
Effect of Nolock Hint in Select Statements
SQL Query to Return Differences Between Two Tables
Spark SQL Window Function With Complex Condition
Humanized or Natural Number Sorting of Mixed Word-And-Number Strings
Where Value in Column Containing Comma Delimited Values
How to Select an Entire Row Which Has the Largest Id in the Table
Why "Extra Characters After Command" Error Shown for the Sed Command Line Shown
The Maximum Recursion 100 Has Been Exhausted Before Statement Completion
MySQL Select Dynamic Row Values as Column Names, Another Column as Value
Insert Data in 3 Tables At a Time Using Postgres
How to Dump the Data of Some Sqlite3 Tables
Group by Behavior When No Aggregate Functions Are Present in the Select Clause
How to Count Unique Items in Field in Access Query