symfony3反向实体映射慢

时间:2017-08-21 09:14:34

标签: php symfony

我有以下实体:

<?php

namespace AppBundle\Entity;

use Doctrine\ORM\Mapping as ORM;
use Doctrine\Common\Collections\ArrayCollection;
use Doctrine\Common\Collections\Collection;

/**
 * Productnum
 *
 * @ORM\Table(name="productnum")
 * @ORM\Entity
 */
class Productnum 
{
  /**
   * @var object
   *
   * @ORM\OneToMany(
   *      targetEntity="AppBundle\Entity\Products",
   *      mappedBy="productnum",
   *      cascade={"persist", "remove"}
   * )
   */
  protected $productnumInverse;

  /**
   * Constructor
   */
  public function __construct()
  {
      $this->productnumInverse = new ArrayCollection();
  }


  /**
   * Get productnumInverse
   *
   * @return Collection
   */
  public function getProductnumInverse()
  {
      return $this->productnumInverse;
  }

}

<?php

namespace AppBundle\Entity;

use Doctrine\ORM\Mapping as ORM;
use Doctrine\Common\Collections\ArrayCollection;
use Doctrine\Common\Collections\Collection;


/**
 * Products
 *
 * @ORM\Table(name="products")
 * @ORM\Entity
 */
class Products
{
   /**
   * @var \AppBundle\Entity\Productnum
   *
   * @ORM\ManyToOne(targetEntity="AppBundle\Entity\Productnum", inversedBy="productnumInverse")
   * @ORM\JoinColumns({
   *   @ORM\JoinColumn(name="productnum_id", referencedColumnName="row_id")
   * })
   */
  public $productnum;

   /**
   * @var object
   *
   * @ORM\OneToMany(
   *      targetEntity="AppBundle\Entity\Product_region",  fetch="EAGER",
   *      mappedBy="productid",
   *      cascade={"persist", "remove"}
   * )
   */
  protected $productInverse;

  /**
   * Constructor
   */
  public function __construct()
  {
      $this->productInverse = new ArrayCollection();
  }
}

<?php

namespace AppBundle\Entity;

use AppBundle\AppBundle;
use AppBundle\Entity\Productnum_filial;
use Doctrine\ORM\Mapping as ORM;

/**
 * Productnum_region
 *
 * @ORM\Table(name="productnum_region")
 * @ORM\Entity
 */
class Productnum_region
{
  //regular getters and setters here...
}

一个映射实体:

<?php

namespace AppBundle\Entity;

use Doctrine\ORM\Mapping as ORM;

/**
 * Product_region
 *
 * @ORM\Table(name="product_region")
 * @ORM\Entity
 */
class Product_region
{
  /**
   * @ORM\Column(name="id", type="integer")
   * @ORM\Id
   * @ORM\GeneratedValue(strategy="IDENTITY")
   */
  private $id;

/**
 * @var \AppBundle\Entity\Products
 *
 * @ORM\ManyToOne(targetEntity="AppBundle\Entity\Products")
 * @ORM\JoinColumns({
 *   @ORM\JoinColumn(name="product_id", referencedColumnName="row_id")
 * })
 * 
 */
private $productid;

/**
 * @var \AppBundle\Entity\Productnum_region
 *
 * @ORM\ManyToOne(targetEntity="AppBundle\Entity\Productnum_region")
 * @ORM\JoinColumns({
 *   @ORM\JoinColumn(name="region_id", referencedColumnName="id")
 * })
 */
private $regionid;
}

在我的控制器中,我得到了以下代码

$sql = sprintf("SELECT p FROM 'AppBundle:Productnum' p");
     $productnums = $em->createQuery($sql)
       ->setFirstResult($start)
       ->setMaxResults($length)
       ->getResult();
     $data = [];
     foreach($productnums as $productnum) {
         $prods = '';
         foreach($productnum->getProductnumInverse() as $product) {
            $filials = [];
            $regions = [];
            if($product && $product->getAllregions()){
                $regions[] = $filials[] = 'All';
                    } elseif($product && $product->getAllfilials()){
                      $filials[] = 'All';
                       $regs = $product->getProductInverse();
                       foreach($regs as $reg){        
                          $regions[] =  $reg->getRegionid()->getName();
                                }
                        }elseif($product){
                            $regs = $product->getProductInverse();
                            foreach($regs as $reg){
                                $fil = $reg->getRegionid()->getFilial()->getName();
                                if(!in_array($fil, $filials)){
                                    $filials[] = $fil;
                                }
                                $regions[] =  $reg->getRegionid()->getName();
                              }
  }
}

问题是在我的本地计算机上这个代码工作正常,但在远程服务器上它运行速度很慢。我在我的本地计算机和服务器上打开了Symfony探查器,并在我的本地计算机上看到了(这没关系)它需要336 DB(总共1.5秒)查询才能完成大部分查询,如下所示;

SELECT t0.id AS id_1, t0.name AS name_2, t0.code AS code_3, t0.filial_id AS filial_id_4 FROM productnum_region t0 WHERE t0.id = ?
Parameters: [0 => 100] 

SELECT t0.row_id AS row_id_1, ...  t0.productnum_id AS productnum_id_21, t22.id AS id_23, t22.product_id AS product_id_24, t22.region_id AS region_id_25 FROM products t0 LEFT JOIN product_region t22 ON t22.product_id = t0.row_id WHERE t0.productnum_id = ?
Parameters: [0 => 945]

在我的服务器上总共有36个查询(总共20秒,BAD),其中一个(可能是最慢的一个)如下:

SELECT t0.row_id AS row_id_1, ... t0.productnum_id AS productnum_id_21, t22.id AS id_23, t22.product_id AS product_id_24, t22.region_id AS region_id_25 
FROM products t0 LEFT JOIN product_region t22 ON t22.product_id = t0.row_id WHERE t0.row_id IN (?)
Parameters: [ 0 => [ 0 => 2, 1 => 97, 2 => 212, 3 => 225, 4 => 297, 5 => 355, 6 => 356, 7 => 482, 8 => 571, 9 => 737, 10 => 789 
...MANY MANY MANY data here...

所以问题是,不同机器上的相同代码会转换为不同的查询以及如何避免这种情况会怎样?

谢谢

1 个答案:

答案 0 :(得分:1)

看起来您的数据库设计或代码可能总体上存在错误,要求您将4个if循环和2个foreach($productnum->getProductnumInverse() as $product) { 语句全部嵌套在一起。

要专门回答您的问题 - 您需要在查询中加入相应的实体 - 教义不会为您执行此操作。所以当你执行此操作时:

$product

通过该循环的每次迭代,Doctrine将单独查询SELECT p FROM 'AppBundle:Productnum' p ,因为它没有在原始查询中选择它。这就是为什么你看到336个数据库查询,当你真的应该只看到一个。而不是:

SELECT p, pi, pip, pir
FROM AppBundle:Productnum p
JOIN p.productnumInverse pi
JOIN pi.product pip
JOIN pi.region pir

您的查询应该更像这样:

{{1}}

这应该会大大减少您正在运行的查询数量 - 理想情况下,您应该使用1个查询来检索所有数据。简而言之,除非您明确告知相关实体,否则Doctrine不会加入相关实体。