[DDC-2381] Pagination query can be simplified when simple joins are applied Created: 31/Mar/13 Updated: 08/Apr/13 |
|
| Status: | Open |
| Project: | Doctrine 2 - ORM |
| Component/s: | ORM |
| Affects Version/s: | 2.3, 2.4 |
| Fix Version/s: | None |
| Security Level: | All |
| Type: | Improvement | Priority: | Minor |
| Reporter: | Sergey Gerdel | Assignee: | Marco Pivetta |
| Resolution: | Unresolved | Votes: | 0 |
| Labels: | paginator | ||
| Attachments: |
|
| Description |
|
Hi. SELECT DISTINCT id0 FROM (SELECT m0_.id AS id0, m0_.title AS title1, m0_.text AS text2, m0_.price AS price3, m0_.originalPrice AS originalPrice4, m0_.condition_type AS condition_type5, m0_.image_1 AS image_16, m0_.image_2 AS image_27, m0_.image_3 AS image_38, m0_.image_4 AS image_49, m0_.image_5 AS image_510, m0_.video AS video11, m0_.contact_email AS contact_email12, m0_.contact_name AS contact_name13, m0_.contact_phone AS contact_phone14, m0_.contact_type AS contact_type15, m0_.published AS published16, m0_.type AS type17, m0_.status AS status18, m0_.highlight AS highlight19, m0_.urgent AS urgent20, m0_.topads AS topads21, m0_.period AS period22, m0_.hits AS hits23, m0_.ip AS ip24, m0_.created_at AS created_at25, m0_.updated_at AS updated_at26 FROM milla_message m0_ INNER JOIN milla_currency m1_ ON m0_.currency_id = m1_.id INNER JOIN milla_category m2_ ON m0_.category_id = m2_.id INNER JOIN milla_region m3_ ON m0_.region_id = m3_.id INNER JOIN milla_city m4_ ON m0_.city_id = m4_.id WHERE m0_.status = 1 ORDER BY m0_.published DESC) dctrn_result LIMIT 20 OFFSET 0 why SELECT DISTINCT %s FROM (%s) dctrn_result ??? |
| Comments |
| Comment by Marco Pivetta [ 31/Mar/13 ] |
|
Not a blocker |
| Comment by Marco Pivetta [ 31/Mar/13 ] |
|
What's the result of `EXPLAIN` on a query without the subquery? |
| Comment by Sergey Gerdel [ 31/Mar/13 ] |
|
explain without the subquery |
| Comment by Marco Pivetta [ 31/Mar/13 ] |
|
Sergey Gerdel that's not the same query. |
| Comment by Marco Pivetta [ 31/Mar/13 ] |
|
Sergey Gerdel this is still using Using index; Using temporary; Using filesort Check your indexes |
| Comment by Sergey Gerdel [ 31/Mar/13 ] |
|
Not in the index problem SELECT DISTINCT id0 FROM (SELECT m0_.id AS id0, m0_.title AS title1, m0_.text AS text2, m0_.price AS price3, m0_.originalPrice AS originalPrice4, m0_.condition_type AS condition_type5, m0_.image_1 AS image_16, m0_.image_2 AS image_27, m0_.image_3 AS image_38, m0_.image_4 AS image_49, m0_.image_5 AS image_510, m0_.video AS video11, m0_.contact_email AS contact_email12, m0_.contact_name AS contact_name13, m0_.contact_phone AS contact_phone14, m0_.contact_type AS contact_type15, m0_.published AS published16, m0_.type AS type17, m0_.status AS status18, m0_.highlight AS highlight19, m0_.urgent AS urgent20, m0_.topads AS topads21, m0_.period AS period22, m0_.hits AS hits23, m0_.ip AS ip24, m0_.created_at AS created_at25, m0_.updated_at AS updated_at26 FROM milla_message m0_ WHERE m0_.status = 1 ORDER BY m0_.published DESC) dctrn_result LIMIT 20 OFFSET 0 Time: 104.614s explain 3 SELECT DISTINCT m0_.id AS id0 FROM milla_message m0_ WHERE m0_.status = 1 ORDER BY m0_.published DESC LIMIT 20 OFFSET 0; Time: 0.001s explain 4 |
| Comment by Marco Pivetta [ 01/Apr/13 ] |
|
Sergey Gerdel the ORM cannot simplify a complex query that way. There may be a conditional on one of the joined results, or generally usage of one of the joined results. Things that could be optimized here are:
The problem I see here is that the chance to spawn random bugs because of the optimization is very high, and you'd have to rewrite `walkSelectStatement` |
| Comment by Marco Pivetta [ 01/Apr/13 ] |
|
Marking as improvement |
| Comment by Sergey Gerdel [ 07/Apr/13 ] |
|
Minor? OK. Programmers may be mistaken in parser reality ORDER BY m0_.published DESC) dctrn_result LIMIT 20 OFFSET 0 |
| Comment by Marco Pivetta [ 07/Apr/13 ] |
|
Sergey Gerdel this problem does not introduce security issues and can be worked around by you while using your own pagination logic. It does not stop you from doing anything, that's why it's minor. |
| Comment by Sergey Gerdel [ 08/Apr/13 ] |
|
ok) |
[DDC-2283] Paginator with orderBy in joined data retrieve bad result Created: 07/Feb/13 Updated: 26/Feb/13 |
|
| Status: | Open |
| Project: | Doctrine 2 - ORM |
| Component/s: | ORM |
| Affects Version/s: | 2.3.2 |
| Fix Version/s: | None |
| Type: | Improvement | Priority: | Minor |
| Reporter: | Jean-Philippe THEVENOUX | Assignee: | Benjamin Eberlei |
| Resolution: | Unresolved | Votes: | 0 |
| Labels: | paginator | ||
| Description |
|
entity A have many entity B If DQL is something like "select A, B from A join B order by A.field1, B.field2" so, if a entity A have 20 entity B (and these sub-entity have all a different b.field2) then there's only 1 A retrieved |
[DDC-2236] SUM(..) with Pagination gives incorrect result Created: 11/Jan/13 Updated: 10/Feb/13 |
|
| Status: | In Progress |
| Project: | Doctrine 2 - ORM |
| Component/s: | Tools |
| Affects Version/s: | 2.2.3 |
| Fix Version/s: | None |
| Type: | Documentation | Priority: | Minor |
| Reporter: | Oleg | Assignee: | Benjamin Eberlei |
| Resolution: | Unresolved | Votes: | 1 |
| Labels: | paginator | ||
| Environment: |
Linux |
||
| Description |
|
https://github.com/whiteoctober/Pagerfanta/issues/69 <?php $pager = new Pagerfanta(new DoctrineORMAdapter($query)); $result = $pager->getCurrentPageResults(); $result = $query->getQuery()->getResult(); Sql for the above: SELECT DISTINCT id0 FROM (SELECT q0_.id AS id0, SUM(q0_.price) AS sclr36 FROM Q q0_ WHERE q0_.id IN (19, 20, 22) GROUP BY q0_.customer_id) dctrn_result LIMIT 30 OFFSET 0 Sql with fetchJoin = false (new DoctrineORMAdapter($query, false)) SELECT q0_.id AS id0, SUM(q0_.price) AS sclr36 FROM Quote q0_ WHERE q0_.id IN (19, 20, 22) GROUP BY q0_.customer_id LIMIT 30 OFFSET 0 |
| Comments |
| Comment by Alexander [ 09/Feb/13 ] |
|
Can you also test this with doctrine >= 2.3? The pagination code changed quite a lot. |
| Comment by Oleg [ 10/Feb/13 ] |
|
Looks like no change composer.json:
then cleared cache but result is same
$query = $this->getDoctrine()->getEntityManager()->getRepository('MyBundle:Invoice')
->createQueryBuilder('q')
->select('q', 'SUM(q.amount) AS amount')
->groupBy('q.customer')
;
95 Connect root@localhost on ** 95 Query SELECT DISTINCT id0 FROM (SELECT i0_.id AS id0, i0_.invoice_num AS invoice_num1, i0_.date AS date2, i0_.amount AS amount3, i0_.vat_amount AS vat_amount4, i0_.amount_paid AS amount_paid5, i0_.md5 AS md56, i0_.is_exported AS is_exported7, i0_.created AS created8, SUM(i0_.amount) AS sclr9 FROM Invoice i0_ GROUP BY i0_.customer_id) dctrn_result LIMIT 30 OFFSET 0 95 Query SELECT i0_.id AS id0, i0_.invoice_num AS invoice_num1, i0_.date AS date2, i0_.amount AS amount3, i0_.vat_amount AS vat_amount4, i0_.amount_paid AS amount_paid5, i0_.md5 AS md56, i0_.is_exported AS is_exported7, i0_.created AS created8, SUM(i0_.amount) AS sclr9, i0_.customer_id AS customer_id10 FROM Invoice i0_ WHERE i0_.id IN ('2') GROUP BY i0_.customer_id 95 Query SELECT i0_.id AS id0, i0_.invoice_num AS invoice_num1, i0_.date AS date2, i0_.amount AS amount3, i0_.vat_amount AS vat_amount4, i0_.amount_paid AS amount_paid5, i0_.md5 AS md56, i0_.is_exported AS is_exported7, i0_.created AS created8, SUM(i0_.amount) AS sclr9, i0_.customer_id AS customer_id10 FROM Invoice i0_ GROUP BY i0_.customer_id 130210 16:08:25 95 Quit But I understand why that happens, it's due to group by and pagination nature. If I do $pager = new Pagerfanta(new DoctrineORMAdapter($query, false)); I get this sql SELECT i0_.id AS id0, i0_.invoice_num AS invoice_num1, i0_.date AS date2, i0_.amount AS amount3, i0_.vat_amount AS vat_amount4, i0_.amount_paid AS amount_paid5, i0_.md5 AS md56, i0_.is_exported AS is_exported7, i0_.created AS created8, SUM(i0_.amount) AS sclr9, i0_.customer_id AS customer_id10 FROM Invoice i0_ LIMIT 30 OFFSET 0 I think it should be noted somewhere that if you do groupBy you should set fetchJoin to false? |
| Comment by Marco Pivetta [ 10/Feb/13 ] |
|
Updating to Documentation issue. |
[DDC-2213] Paginator does not work with composite primary key entity Created: 25/Dec/12 Updated: 23/Jan/13 |
|
| Status: | Open |
| Project: | Doctrine 2 - ORM |
| Component/s: | ORM, Tools |
| Affects Version/s: | 2.3.1 |
| Fix Version/s: | None |
| Security Level: | All |
| Type: | New Feature | Priority: | Major |
| Reporter: | Stanislav Anisimov | Assignee: | Benjamin Eberlei |
| Resolution: | Unresolved | Votes: | 1 |
| Labels: | composed, key, paginator | ||
| Environment: |
php 5.4 |
||
| Description |
|
Paginator does not work with composed primary key. "Single id is not allowed on composite primary key in entity" exception is thrown here Only first column values are fetched while retrieving primary keys here |
| Comments |
| Comment by Marco Pivetta [ 23/Jan/13 ] |
|
Limitation was confused by issue reporter and considered bug |
[DDC-1958] pager produces wrong results on postgresql Created: 30/Jul/12 Updated: 09/Apr/13 Resolved: 12/Nov/12 |
|
| Status: | Resolved |
| Project: | Doctrine 2 - ORM |
| Component/s: | Tools |
| Affects Version/s: | 2.3 |
| Fix Version/s: | 2.3.1 |
| Security Level: | All |
| Type: | Bug | Priority: | Major |
| Reporter: | Miha Vrhovnik | Assignee: | Benjamin Eberlei |
| Resolution: | Fixed | Votes: | 1 |
| Labels: | paginator | ||
| Environment: |
|
||
| Description |
|
The query build by pager to get the subset of PKs to fetch produces wrong results on potgresql (and probably any database), that conforms to the SQL standard. The standard says, that if you wish to have the results in specific order, then you have to specify that by using an ORDER BY clause. If such a clause is not present the database can return the results in whatever order it sees fit. Testcase fixtures:
CREATE TABLE test (
id integer,
name text
);
INSERT INTO test VALUES (1, 'c');
INSERT INTO test VALUES (2, 'a');
INSERT INTO test VALUES (3, 'e');
INSERT INTO test VALUES (4, 'b');
INSERT INTO test VALUES (5, 'd');
INSERT INTO test VALUES (6, 'a');
INSERT INTO test VALUES (7, 'g');
INSERT INTO test VALUES (8, 'h');
INSERT INTO test VALUES (9, 'e');
INSERT INTO test VALUES (10, 'j');
Passing f.e.
$qb = $this->repository
->createQueryBuilder('t')
->select('t')
->setFirstResult(0)
->setMaxResults(5)
->addOrderBy('t.name', 'ASC')
to pager produces SQL like this modified for readability
SELECT DISTINCT id FROM (
SELECT id, name FROM test ORDER BY name
) dctrn_result
LIMIT 5 OFFSET 0
Now there is nothing wrong with this modified query per se, but there is no ORDER BY clause in the outer query so according to the standard the DB can choose whatever order it seems fit. Now mysql chooses the same order, but postgresql does not and it's probably not the only DB doing so. If you are interested in the results, this is the output I'm seeing:
I and my coworker came to the standard compliant solution it was also tested on the dataset above on both postgresql and mysql and it produced equal results. We have found only one corner case this won't work and IMHO that can't be fixed. The problem is when you do a sort on a field from a table that is in 1:n relation to the main table.. e.g tables posts and tags, where one post can have a multiple tags and you want your results sorted by a tag. Recipe for a correct query is:
so if I take the example from above the SQL should look like this:
SELECT id FROM (
SELECT DISTINCT id, name FROM (
SELECT id, name FROM test
) dctrn_result_inner
ORDER BY name, id LIMIT 5 OFFSET 0
) dctrn_result
|
| Comments |
| Comment by Jean-Philippe THEVENOUX [ 08/Nov/12 ] |
|
I reproduce same problem with Postgres 7.4, Doctrine 2.3 whereas with doctrine 2.2, all is fine |
| Comment by Raymond Kolbe [ 09/Apr/13 ] |
|
http://www.doctrine-project.org/jira/browse/DDC-1800 This relates. I just published a PR for an Oracle fix, but your solution appears to work for Oracle as well (issue is the same). |
[DDC-1935] Not all identifier properties can be found in the ResultSetMapping: id Created: 19/Jul/12 Updated: 29/Jul/12 Resolved: 29/Jul/12 |
|
| Status: | Resolved |
| Project: | Doctrine 2 - ORM |
| Component/s: | Tools |
| Affects Version/s: | Git Master |
| Fix Version/s: | None |
| Security Level: | All |
| Type: | Bug | Priority: | Major |
| Reporter: | dquintard | Assignee: | Benjamin Eberlei |
| Resolution: | Duplicate | Votes: | 0 |
| Labels: | paginator | ||
| Description |
|
$qb1 = $em->createQueryBuilder()->select('c.id'); $doctrinePaginator = new \Doctrine\ORM\Tools\Pagination\Paginator($query1); But works fine with $query2 ! $qb2 = $em->createQueryBuilder()->select('c'); $doctrinePaginator = new \Doctrine\ORM\Tools\Pagination\Paginator($query2); |
| Comments |
| Comment by dquintard [ 20/Jul/12 ] |
|
It seems that this issue is the same that: |
| Comment by Benjamin Eberlei [ 29/Jul/12 ] |
|
Duplicate of |
[DDC-1927] Pagination of a SELECT of specific fields results in a RuntimeException Created: 16/Jul/12 Updated: 18/Apr/13 Resolved: 25/Nov/12 |
|
| Status: | Resolved |
| Project: | Doctrine 2 - ORM |
| Component/s: | ORM |
| Affects Version/s: | Git Master |
| Fix Version/s: | None |
| Security Level: | All |
| Type: | Bug | Priority: | Major |
| Reporter: | Zacharias Luiten | Assignee: | Benjamin Eberlei |
| Resolution: | Can't Fix | Votes: | 0 |
| Labels: | paginator | ||
| Description |
|
When paginating a DQL string which selects specific fields it results in the following error: PHP Fatal error: Uncaught exception 'RuntimeException' with message 'Not all identifier properties can be found in the ResultSetMapping: id' NOT working: 'SELECT c.id, c.number FROM Application\Entity\Course c' Setting hydration mode to scalar results makes no difference. Gist to example code and stack trace: https://gist.github.com/d5cd6d0b0ac28e722dd7 |
| Comments |
| Comment by Benjamin Eberlei [ 29/Jul/12 ] |
|
First results: This is very complicated to support, the pagination was designed for entity results. I have to check this when I have more time. |
| Comment by dquintard [ 08/Aug/12 ] |
|
Hi Benjamin, |
| Comment by Matt Pinkston [ 13/Nov/12 ] |
|
The reason this error occurs is because the Paginator creates its own ResultSetMapping and relies on the SqlWalker to configure it (see Doctrine\ORM\Query\SqlWalker::walkSelectExpression). Doctrine will interpret each field in the first example (SELECT c.id, c.number...) as a PathExpression and add it to the result set mapping as a scalar result. This makes it impossible for the paginator to reliably know which field can be considered an identifier. A quick fix might be to re-write the query to use PartialObjectExpression: SELECT partial c. {id, number}... (edited after a re-read and realization that a custom ResultSetMapping wouldn't cut it) |
| Comment by Benjamin Eberlei [ 25/Nov/12 ] |
|
Allowing generic+complex pagination on scalar results is impossible for us, closing as can't fix. Just use LIMITs yourself here or as suggested partial objects. |
| Comment by Stefano [ 18/Apr/13 ] |
|
imho this pagination feature is quite useless if we are forced to fetch the complete Entity. Take for example a big table with a lot of data: extracting all the infos will take a lot of time... There should be a way to support the first query type |
[DDC-1918] Paginator gives strange results in the end of the resultset Created: 10/Jul/12 Updated: 29/Aug/12 Resolved: 29/Aug/12 |
|
| Status: | Resolved |
| Project: | Doctrine 2 - ORM |
| Component/s: | DQL |
| Affects Version/s: | Git Master |
| Fix Version/s: | Git Master |
| Security Level: | All |
| Type: | Bug | Priority: | Major |
| Reporter: | Alessandro Tagliapietra | Assignee: | Benjamin Eberlei |
| Resolution: | Fixed | Votes: | 0 |
| Labels: | paginator | ||
| Environment: |
Mac OSX MAMP (apache + php 5.3.6 + mysql 5) |
||
| Description |
|
Hello,
SELECT DISTINCT i0_.id AS id0, i0_.creation_time AS creation_time1 FROM Image i0_ LEFT JOIN Tag t1_ ON i0_.id = t1_.image WHERE i0_.owner = 1 ORDER BY i0_.creation_time DESC LIMIT 9 OFFSET 27 it returns 2 images, correct, so the final query is SELECT i0_.id AS id0, i0_.name AS name1, i0_.status AS status2, i0_.last_processing AS last_processing3, i0_.creation_time AS creation_time4, i0_.height AS height5, i0_.width AS width6, i0_.layers AS layers7, i0_.foldersize AS foldersize8, i0_.sourcesize AS sourcesize9, i0_.title AS title10, i0_.description AS description11, i0_.originalUrl AS originalUrl12, i0_.private AS private13, i0_.watermark AS watermark14, i0_.favorite AS favorite15, t1_.id AS id16, t1_.tag AS tag17, i0_.owner AS owner18, t1_.image AS image19 FROM Image i0_ LEFT JOIN Tag t1_ ON i0_.id = t1_.image WHERE i0_.owner = 1 AND i0_.id IN (?, ?) ORDER BY i0_.creation_time DESC with parameters the 2 images id, still correct.
SELECT DISTINCT i0_.id AS id0, i0_.creation_time AS creation_time1 FROM Image i0_ LEFT JOIN Tag t1_ ON i0_.id = t1_.image WHERE i0_.owner = 1 ORDER BY i0_.creation_time DESC LIMIT 9 OFFSET 29 which doesn't get any result, but it still goes on querying without id filter: SELECT i0_.id AS id0, i0_.name AS name1, i0_.status AS status2, i0_.last_processing AS last_processing3, i0_.creation_time AS creation_time4, i0_.height AS height5, i0_.width AS width6, i0_.layers AS layers7, i0_.foldersize AS foldersize8, i0_.sourcesize AS sourcesize9, i0_.title AS title10, i0_.description AS description11, i0_.originalUrl AS originalUrl12, i0_.private AS private13, i0_.watermark AS watermark14, i0_.favorite AS favorite15, t1_.id AS id16, t1_.tag AS tag17, i0_.owner AS owner18, t1_.image AS image19 FROM Image i0_ LEFT JOIN Tag t1_ ON i0_.id = t1_.image WHERE i0_.owner = 1 ORDER BY i0_.creation_time DESC LIMIT 9 OFFSET 29 and it gets the last 2 images maybe due the fetch join like as i'm not using paginator. |
| Comments |
| Comment by Alessandro Tagliapietra [ 15/Aug/12 ] |
|
No updates? |
| Comment by Benjamin Eberlei [ 15/Aug/12 ] |
|
Sorry I am swamped with work at the moment and other important projects, I couldnt look into it yet. |
| Comment by Alessandro Tagliapietra [ 27/Aug/12 ] |
|
Commenting the if this part: Paginator.php
// don't do this for an empty id array
//if (count($ids) > 0) {
$namespace = WhereInWalker::PAGINATOR_ID_ALIAS;
$whereInQuery->setHint(Query::HINT_CUSTOM_TREE_WALKERS, array('Doctrine\ORM\Tools\Pagination\WhereInWalker'));
$whereInQuery->setHint(WhereInWalker::HINT_PAGINATOR_ID_COUNT, count($ids));
$whereInQuery->setFirstResult(null)->setMaxResults(null);
foreach ($ids as $i => $id) {
$i++;
$whereInQuery->setParameter("{$namespace}_{$i}", $id);
}
//}
make it works, for me is better, because if you don't have results to show as the result of the select distinct is empty you shouldn't have any result at all. Because if you don't add the "WHERE IN" query you get the results joined with no difference between using or not the paginator. |
| Comment by Benjamin Eberlei [ 29/Aug/12 ] |
|
This is weird, an empty IN() normally produces an error. We could add a non matching condition instead, like 0 = 1. |
| Comment by Alessandro Tagliapietra [ 29/Aug/12 ] |
|
Not really, in my case (mysql) it creates the following query: SELECT i0_.id AS id0, i0_.name AS name1, i0_.status AS status2, i0_.last_processing AS last_processing3, i0_.creation_time AS creation_time4, i0_.height AS height5, i0_.width AS width6, i0_.layers AS layers7, i0_.foldersize AS foldersize8, i0_.sourcesize AS sourcesize9, i0_.title AS title10, i0_.description AS description11, i0_.originalUrl AS originalUrl12, i0_.private AS private13, i0_.watermark AS watermark14, i0_.favorite AS favorite15, t1_.id AS id16, t1_.tag AS tag17, i0_.owner AS owner18, t1_.image AS image19 FROM Image i0_ LEFT JOIN Tag t1_ ON i0_.id = t1_.image WHERE i0_.owner = 1 AND i0_.id IS NULL ORDER BY i0_.creation_time DESC so it sets "AND i0_.id IS NULL" which solves the problem, the best solution for me is to directly return an empty set if the DISTINCT gives no results. |
| Comment by Alessandro Tagliapietra [ 29/Aug/12 ] |
|
Solved with commit bc2476f342ceda98559b87a314806581cf969796 |