Symfony4 - Доктрина разбиения на страницы низкая производительность подсчета запросов (634484 записей) - PullRequest
2 голосов
/ 21 июня 2019

У меня есть таблица (без отношений), которая нуждается в нумерации страниц. Для этого я использую инструмент Doctrine Pagination.

Таблица содержит 634484 записей. Doctrine выполняет следующие запросы:

SELECT p0_.id AS id_0, p0_.name AS name_1, p0_.level AS level_2, p0_.alignment AS alignment_3, p0_.account_id AS account_id_4 FROM player.player p0_ ORDER BY p0_.level DESC LIMIT 25;

объяснить:

id  select_type table   partitions  type    possible_keys   key key_len ref rows    filtered    Extra
1   SIMPLE  p0_     index       search  1       25  100.00

время исполнения: 1,04 мс

проблемный запрос :

SELECT 
  COUNT(*) AS dctrn_count 
FROM 
  (
    SELECT 
      DISTINCT id_0 
    FROM 
      (
        SELECT 
          p0_.id AS id_0, 
          p0_.name AS name_1, 
          p0_.level AS level_2, 
          p0_.alignment AS alignment_3 
        FROM 
          player.player p0_ 
        ORDER BY 
          p0_.level DESC
      ) dctrn_result
  ) dctrn_table

объяснить:

id  select_type table   partitions  type    possible_keys   key key_len ref rows    filtered    Extra
1   PRIMARY <derived2>      ALL                 634484  100.00  
2   DERIVED p0_     ALL PRIMARY             634484  100.00  Using temporary

Время выполнения: 2414,72 мс

Doctrine использует ключевое слово DISTINCT, чтобы узнать, сколько строк содержится в таблице, что, вероятно, является причиной того, что запрос выполнялся так долго.

Время выполнения без ключевого слова DISTINCT: 1,26 мс

EDIT

Repository:

public function findLatestPaginated(int $page = 1): array
{
    $queryBuilder = $this->createQueryBuilder('c')
        ->addOrderBy('c.level', 'DESC');


    return $this->paginate($queryBuilder, $page, Character::PER_PAGE);
}

Метод пагината:

<?php

namespace App\Core\Repository;

use Doctrine\Bundle\DoctrineBundle\Repository\ServiceEntityRepository as BaseServiceEntityRepository;
use Doctrine\ORM\Query;
use Doctrine\ORM\QueryBuilder;
use Doctrine\ORM\Tools\Pagination\Paginator;

abstract class ServiceEntityRepository extends BaseServiceEntityRepository
{
    protected function paginate(QueryBuilder $queryBuilder, int $currentPage, int $perPage)
    {
        $currentPage = $currentPage < 1 ? 1 : $currentPage;
        $firstResult = ($currentPage - 1) * $perPage;

        /** @var Query $query */
        $query = $queryBuilder
            ->setFirstResult($firstResult)
            ->setMaxResults($perPage)
            ->getQuery();

        $paginator = new Paginator($query, false);
        $numResults = $paginator->count();
        $hasPreviousPage = $currentPage > 1;
        $hasNextPage = ($currentPage * $perPage) < $numResults;

        return [
            'result' => $paginator->getIterator(),
            'currentPage' => $currentPage,
            'hasPreviousPage' => $hasPreviousPage,
            'hasNextPage' => $hasNextPage,
            'previousPage' => $hasPreviousPage ? $currentPage - 1 : null,
            'nextPage' => $hasNextPage ? $currentPage + 1 : null,
            'numPages' => (int)ceil($numResults / $perPage),
            'haveToPaginate' => $numResults > $perPage,
        ];
    }
}

Вопросы:

  • Почему Doctrine использует ключевое слово в запросе couting? (id, который является первичным ключом)
  • Как я могу предотвратить это?

1 Ответ

0 голосов
/ 22 июня 2019

Вы можете отключить ключевое слово DISTINCT из запроса подсчета, выполнив:

use Doctrine\ORM\Tools\Pagination\CountWalker;

$query->setHint(CountWalker::HINT_DISTINCT, false);

Взглянув на Doctrine / ORM / Tools / Pagination / Paginator кажется, что это помешает добавить отчет о выражении в запрос подсчета.

...