How do I create an efficient DQL statement, to match my efficient SQL when doing a simple LEFT JOIN?
Asked Answered
C

1

2

I can craft a simple SQL that returns 1 when there is an item with requested id and corresponding model that matches FS-%, and 0 otherwise.

But when I try to write it as DQL, I fail in all spectacular ways. See EXPLAIN results below.

Question: How do I write an efficient DQL?

SQL (efficient)

select count(*) 
from item 
left join product on item.product_id = product.id 
where item.id=2222 and product.model like "FS-%";

Using Explain:

+----+-------------+---------+-------+--------------------+---------+---------+-------+------+-------+
| id | select_type | table   | type  | possible_keys      | key     | key_len | ref   | rows | Extra |
+----+-------------+---------+-------+--------------------+---------+---------+-------+------+-------+
|  1 | SIMPLE      | item    | const | PRIMARY,product_id | PRIMARY | 4       | const |    1 |       |
|  1 | SIMPLE      | product | const | PRIMARY            | PRIMARY | 4       | const |    1 |       |
+----+-------------+---------+-------+--------------------+---------+---------+-------+------+-------+

DQL (NOT efficient)

 $this->getEntityManager()
        ->createQueryBuilder()
        ->select('count(i)')
        ->from(Item::class, 'i')
        ->leftJoin(Product::class, 'p')
        ->where('i.id = :id')
        ->andWhere('p.model like :model')
        ->setParameter('id', 2222)
        ->setParameter('model', 'FS-%')
        ->getQuery()->getSingleResult();

Resulting SQL:

SELECT * FROM item i0_ LEFT JOIN product p1_
        ON (i0_.id = 409264 AND p1_.model LIKE 'FS-%');

Using Explain:

+----+-------------+-------+------+---------------+------+---------+------+--------+-------+
| id | select_type | table | type | possible_keys | key  | key_len | ref  | rows   | Extra |
+----+-------------+-------+------+---------------+------+---------+------+--------+-------+
|  1 | SIMPLE      | i0_   | ALL  | NULL          | NULL | NULL    | NULL | 276000 |       |
|  1 | SIMPLE      | p1_   | ALL  | NULL          | NULL | NULL    | NULL |    564 |       |
+----+-------------+-------+------+---------------+------+---------+------+--------+-------+
2 rows in set (0.00 sec)            

Note: I used https://mcmap.net/q/1276746/-select-count-in-doctrine-dql-with-left-join-manytomany-unidirectional-relation-where-user-does-not-have-relation-specific-group to help me write the DQL.

Candless answered 6/6, 2016 at 23:10 Comment(1)
If you are happy with sql then use sql. $em->getConnection will return a thin wrapper over pdo. As you have discovered, dql really wants to deal with objects.Upbraiding
T
1

in your case I would have tried this query:

$this->getEntityManager()
        ->createQueryBuilder()
        ->select('count(i)')
        ->from(Item::class, 'i')
        ->leftJoin(Product::class, 'p', 'WITH', 'i.product = p.id')
        ->where('i.id = :id')
        ->andWhere('p.model like :model')
        ->setParameter('id', 2222)
        ->setParameter('model', 'FS-%')
        ->getQuery()->getSingleScalarResult();

change product in i.product to your property name

Templet answered 7/6, 2016 at 16:58 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.