选择字段以优化查询的最佳方法

时间:2015-01-26 14:28:16

标签: symfony doctrine-orm

我是一个社交网络,我需要对其进行优化。我搜索并测试(本地)如果我为查询选择字段,我认为我可以赢得30%的性能。 我是对的吗?或者它不会改变?

Actualy当我写一个查询时,我这样写:

public function getNewsfeed($count) {

    $q = $this->createQueryBuilder('n')
                ->leftJoin('n.sender', 'u')
                    ->addSelect('u')
                ->orderBy('n.id', 'DESC')
                ->setMaxResults($count);

    return $q->getQuery()->getResult();
}

所以这给我这个:

SELECT n0_.id AS id0, n0_.type AS type1, n0_.data AS data2, n0_.reference AS reference3, n0_.date AS date4, f1_.username AS username5, f1_.username_canonical AS username_canonical6, f1_.email AS email7, f1_.email_canonical AS email_canonical8, f1_.enabled AS enabled9, f1_.salt AS salt10, f1_.password AS password11, f1_.last_login AS last_login12, f1_.locked AS locked13, f1_.expired AS expired14, f1_.expires_at AS expires_at15, f1_.confirmation_token AS confirmation_token16, f1_.password_requested_at AS password_requested_at17, f1_.roles AS roles18, f1_.credentials_expired AS credentials_expired19, f1_.credentials_expire_at AS credentials_expire_at20, f1_.id AS id21, f1_.user_ip AS user_ip22, f1_.name AS name23, f1_.avatar AS avatar24, f1_.birthday AS birthday25, f1_.sex AS sex26, f1_.city AS city27, f1_.country AS country28, f1_.last_activity AS last_activity29, f1_.warning AS warning30, f1_.status AS status31, f1_.notification AS notification32, f1_.visitor AS visitor33, f1_.friend AS friend34, f1_.message AS message35, f1_.antiflood AS antiflood36, f1_.wantmail AS wantmail37, f1_.autodestruction AS autodestruction38, f1_.suspended AS suspended39, n0_.sender_id AS sender_id40, f1_.profil_id AS profil_id41 FROM newsfeed n0_ LEFT JOIN fos_user f1_ ON n0_.sender_id = f1_.id ORDER BY n0_.id DESC LIMIT 15

你能看到吗,它已经很久了。我不需要电子邮件,密码等。我在Stack上搜索了如何选择带有学说的字段,以及唯一的工作方式是这样的:

public function getNewsfeed($count) {

    $q = $this->createQueryBuilder('n')
                ->select('partial n.{id, type, reference, data, date}')
                ->leftJoin('n.sender', 'u')
                    ->addSelect('partial u.{id, name, avatar, roles, sex, last_activity, suspended}')
                ->orderBy('n.id', 'DESC')
                ->setMaxResults($count);

    return $q->getQuery()->getResult();
}

它给我一个更小的查询:

SELECT n0_.id AS id0, n0_.type AS type1, n0_.data AS data2, n0_.reference AS reference3, n0_.date AS date4, f1_.roles AS roles5, f1_.id AS id6, f1_.name AS name7, f1_.avatar AS avatar8, f1_.sex AS sex9, f1_.last_activity AS last_activity10, f1_.suspended AS suspended11, n0_.sender_id AS sender_id12, f1_.profil_id AS profil_id13 FROM newsfeed n0_ LEFT JOIN fos_user f1_ ON n0_.sender_id = f1_.id ORDER BY n0_.id DESC LIMIT 15

所以我有两个问题:

  1. 如果我选择字段,我真的会赢得很多表现吗?
  2. 这是最好的方法吗?或者您还有另一种选择字段的方法?
  3. 感谢您抽出时间抱歉我的新手问题。

2 个答案:

答案 0 :(得分:0)

绝对会影响性能。

对于简单/小数据,成本很低。但是,如果您有BLOB或类似的东西,那么,您可以获得图片 - 服务器只需要时间来获取并将所有数据传输到PHP。从Doctrine的角度来看,如果您有相关的实体,则不必浪费额外的时间来创建Proxy个对象。

如果您希望保留某个模型的对象模型,请使用PARTIAL,如上所述。

如果您选择不保留对象模型,则可以挤出额外的性能,例如,如果您计划在客户端之后立即发送数据:

$data = $qb->getQuery()->getArrayResult();

缺点是你不能再使用getter / setter了。

旁注:

术语optimize与缓存密切相关。鉴于您正在构建社交网络,您是否考虑过使用一个社交网络? :)

答案 1 :(得分:0)

如果你想进行优化我建议使用存储过程并将其映射到ResultSetMapping,并注意我在我的应用程序中实现的有三层的实例:

1.-接口

<?php

namespace YourBundle\Entity\Repository;

use Doctrine\ORM\Query\ResultSetMapping;

interface BaseRepositoryInterface 
{
    public function _getByFilters($sp, ResultSetMapping $rsm, array $vars);

    public function _getOneResult($sp, ResultSetMapping $rsm, array $vars);

    public function _add($sp, ResultSetMapping $rsm, array $vars);

    public function _update($sp, ResultSetMapping $rsm, array $vars);

    public function _recordCnt($sp, ResultSetMapping $rsm, array $vars);

    public function _pages($sp, ResultSetMapping $rsm, array $vars, $size);

    public function generateArgs(array $vars, $sql);
}

2.-实施

<?php

namespace YourBundle\Entity\Repository;

use Doctrine\ORM\EntityRepository;
use Doctrine\ORM\Query\ResultSetMapping;

class BaseSpRepository extends EntityRepository implements BaseRepositoryInterface
{
    public function _getByFilters($sp, ResultSetMapping $rsm, array $vars) {
        list($sql, $parameters) = $this->generateArgs($vars, $sp);
        $this->clear();

        return $this->getEntityManager()
            ->createNativeQuery($sql, $rsm)
            ->setParameters($parameters)
            ->getResult();
    }

    public function _getOneResult($sp, ResultSetMapping $rsm, array $vars) {
        $result = $this->_getByFilters($sp, $rsm, $vars);

        if (count($result) < 1) {
            return null;
        }

        return array_shift($result);
    }

    public function _add($sp, ResultSetMapping $rsm, array $vars) {
        $result = array();
        list($sql, $parameters) = $this->generateArgs($vars, $sp);
        $this->clear();

        $result = $this->getEntityManager()
            ->createNativeQuery($sql, $rsm)
            ->setParameters($parameters)
            ->getResult();

        if (count($result) == 1)
            $result = $result[0];

        return $result;
    }

    public function _update($sp, ResultSetMapping $rsm, array $vars) {
        return $this->_add($sp, $rsm, $vars);
    }

    public function _pages($sp, ResultSetMapping $rsm, array $vars, $size = 10) {
        $result = array();
        list($sql, $parameters) = $this->generateArgs($vars, $sp);
        $this->clear();

        $result = $this->getEntityManager()
            ->createNativeQuery($sql, $rsm)
            ->setParameters($parameters)
            ->getResult();

        if (count($result) > 0)
            $result = array_values($result[0])[0];

        if (($result % $size) > 0)
            return intval($result / $size) + 1;

        return intval($result / $size);
    }

    public function _recordCnt($sp, ResultSetMapping $rsm, array $vars) {
        $result = array();
        list($sql, $parameters) = $this->generateArgs($vars, $sp);
        $this->clear();

        $result = $this->getEntityManager()
            ->createNativeQuery($sql, $rsm)
            ->setParameters($parameters)
            ->getResult();

        if (count($result) > 0)
            return array_values($result[0])[0];

        return 0;
    }

    public function generateArgs(array $vars, $sql) {
        $parameters = array();
        $wheres = array();
        $sql = sprintf('EXEC %s', $sql);

        if (count($vars) > 0) {
            $keys = array_keys($vars);

            foreach ($keys as $k) {
                $wheres[] = sprintf('@%s = :%s', $k, $k);

                if ($this->validateDate($vars[$k])) {
                    $date = new \DateTime(str_replace('/', '-', trim($vars[$k])));

                    $parameters[$k] = $date;
                } else if (preg_match('/^\d+\.?\d{0,2}$/', $vars[$k])) {
                    $parameters[$k] = floatval(str_replace(',', '.', $vars[$k]));
                } else {
                    $parameters[$k] = $vars[$k];
                }
            }

            $sql = implode(" ", array($sql, implode(",", $wheres)));
        }

        return array($sql, $parameters);
    }

    private function validateDate($date, $format = 'd/m/Y') {
        $d = \DateTime::createFromFormat($format, $date);

        return $d && $d->format($format) == $date;
    } 
}

3.-如何应用基本模型的示例

<?php

namespace YourBundle\Entity\Repository;

use Doctrine\ORM\Query\ResultSetMapping;
use YourBundle\Entity\Repository\BaseSpRepository;

class DriverRepository extends BaseSpRepository
{
    private function getRsm($type = 'a') {
        $rsm = new ResultSetMapping();

        switch ($type) {
            case 'a':
                $rsm->addEntityResult('YourBundle\Entity\Driver', 'd');
                $rsm->addFieldResult('d', 'driverId', 'id');
                $rsm->addFieldResult('d', 'dni', 'dni');
                $rsm->addFieldResult('d', 'names', 'names');
                $rsm->addFieldResult('d', 'lastNames', 'lastNames');
                $rsm->addFieldResult('d', 'license', 'license');
                $rsm->addFieldResult('d', 'register', 'register');
                $rsm->addFieldResult('d', 'revalidationAt', 'revalidationAt');
                $rsm->addJoinedEntityResult('YourBundle\Entity\LicenseCategory', 'lc', 'd', 'licenseCategory');
                $rsm->addFieldResult('lc', 'licenseCategoryId', 'id');
                $rsm->addFieldResult('lc', 'licenseCategoryName', 'name');
                $rsm->addJoinedEntityResult('YourBundle\Entity\Provider', 'p', 'd', 'provider');
                $rsm->addFieldResult('p', 'providerId', 'id');
                $rsm->addFieldResult('p', 'providerName', 'name');
                break;
            case 'b':
                $rsm->addEntityResult('YourBundle\Entity\Driver', 'd');
                $rsm->addScalarResult('recordCnt', 'd');
                break;
            case 'c':
                $rsm->addEntityResult('YourBundle\Entity\Driver', 'd');
                $rsm->addFieldResult('d', 'id', 'id');
                $rsm->addFieldResult('d', 'dni', 'dni');
                $rsm->addFieldResult('d', 'license', 'license');
                $rsm->addFieldResult('d', 'register', 'register');
                break;
        }

        return $rsm;
    }

    public function getByFilters(array $vars = array()) {
        return $this->_getByFilters("usp_drivers_index", $this->getRsm(), $vars);
    }

    public function getOneResult(array $vars = array()) {
        return $this->_getOneResult("usp_drivers_index", $this->getRsm(), $vars);
    }

    public function add(array $vars = array()) {
        return $this->_add("usp_drivers_add", $this->getRsm(), $vars);
    }

    public function update(array $vars = array()) {
        return $this->_update("usp_drivers_edit", $this->getRsm(), $vars);
    }

    public function pages(array $vars = array(), $size = 10) {
        return $this->_pages("usp_drivers_record_count", $this->getRsm('b'), $vars, $size);
    }

    public function exist(array $vars = array()) {
        return $this->_getByFilters("usp_drivers_check_if_exist", $this->getRsm('c'), $vars);
    }
}