【问题标题】:October cms long load time for query10月cms长查询加载时间
【发布时间】:2018-06-23 12:40:45
【问题描述】:

我正在尝试优化这个需要很长时间才能加载的嵌套查询。

基本思路是获取属于一个部门的所有系列,中间是属于多部门多系列的产品。 表结构

部门**产品**系​​列

在模型中关系定义如下部门模型

public $belongsToMany  = [
      'products' => [
        '\depcore\parts\Models\Product',
        'table' => 'depcore_parts_products_departments',
      ],
      // 'series' => [
      //   '\depcore\parts\Models\Series',
      //   'table' => 'depcore_parts_products_series',
      // ]
    ];

系列型号

public $hasMany = [
      'products' => [
        '\depcore\parts\Models\Product',
        'table' => 'depcore_parts_products_departments',
      ]
    ];

产品型号

public $belongsToMany = [
      'series' => [
        'depcore\parts\Models\Series',
        'table' => 'depcore_parts_products_series',
        'order' => 'name',
        ],
      'departments' => [
        'depcore\parts\Models\Department',
        'table' => 'depcore_parts_products_departments',
        // 'order' => 'name'
        ]
    ];

在部门模型中,我创建了一个方法来检索属于部门的所有系列,在分析后会导致一些严重的性能问题

public function series (){

      $seriesArray = array( );
      $products = $this->products()->remember(100)->get();
      foreach ($products as $product) {
        $productSeries = $product->series()->remember(100)->get();
        foreach ($productSeries as $series) {
            if (!isset($seriesArray[$series->id]) and $series->published )
                $seriesArray[$series->id] = $series;
        }
      }

     return \Illuminate\Database\Eloquent\Collection::make($seriesArray);

    }

我想这可以通过原始 SQL 或在活动记录中更好地实现来完成,但我对两者都感到困惑。

我后来添加了remember() 方法,但没有结果。 目前网页的加载时间约为 20 秒。立即删除此代码时。

感谢任何建议。

从表结构我猜这将是运行适当的 SQL 命令(命令)以获得所需的结果

SELECT DISTINCT series_id FROM depcore_parts_products_series WHERE product_id IN (SELECT DISTINCT product_id FROM depcore_parts_products_departments WHERE department_id = 3);

这个查询只在中间表上运行,并得到series_id的正确结果(在phpmyadmin中)(以department_id = 3为例)

使用 Hardik Satasiya 代码,我必须更改几行,否则视图将不会显示任何系列,只是空行。

public function series (){

        $sql = 'SELECT DISTINCT series_id FROM depcore_parts_products_series WHERE product_id IN (SELECT DISTINCT product_id FROM depcore_parts_products_departments WHERE department_id = :dep_id)';

        $data = ['dep_id' => $this->id];
        $query = \DB::select($sql, $data);
        $data = $query;

        $ids = array();
        // I had to rewrite this pare and make it more inelegant still 
        // but the refresh method appeared to made it step out the execution cycle 
        foreach ($data as $key => $value) {
            $ids[] = $value->series_id;
        }

        // in $data we are passing only id information
        // so this records have only id, not db all attributes
        // what ever you pass in $data will become model attributes if its in list
        $collection = \depcore\parts\Models\Series::hydrate($ids);
        return Series::published()->whereIn('id',$ids)->get();

    }

区块部分

<div class="element-grid">
    <h4 {% if hideChildren|length and departmentModel.id not in filters.departments %} class='inactive' {% endif %}  ><a href="{{ url('/')}}/parts?Filter[departments][]={{ departmentModel.id }}">{{ departmentModel.name }}</a></h4>
    {% if not hideChildren|length %}
    <div class="list">
      <div class="block left-block">
          {% if departmentModel.getChildren|length %}
              <ul class='departments'>
                {% for child in departmentModel.getChildren %}
                     {% if child.published %}
                        <li><strong><a href="{{ url('/')}}/parts?Filter[departments][]={{ departmentModel.id }}&Filter[departments][]={{ child.id }}">{{ child.name }}</a></li></strong>
                     {% endif %}

                {% endfor %}
              </ul>
          {% endif %}
         <ul class="series">
           {% for series in departmentModel.departmentSeries.series|slice(0,10) %}
             <li><a href="{{ url('/')}}/parts?Filter[departments][]={{ departmentModel.id }}&{{ departmentModel.departmentSeries.childrenString }}&Filter[series][]={{ series.id }}">{{ series.name }}</a></li>
           {% endfor %}
         </ul>
        </div>
        <div class="block right-block">
             <ul class="series series-right">
                {% for series in departmentModel.departmentSeries.series|slice(10,length) %}
                    <li><a href="{{ url('/')}}/parts?Filter[departments][]={{ departmentModel.id }}&{{ departmentModel.departmentSeries.childrenString }}&Filter[series][]={{ series.id }}">{{ series.name }}</a></li>
                {% endfor %}
             </ul>
        </div>
    </div>
    <img src="{{ departmentModel.image.file_name | media }}" alt="">
    {% endif %}
</div>

departmentSeries 范围。

public function scopeDepartmentSeries( $query ){
        $children = $query->getModel()->getChildren();
        // dd($children);
        if ( count( $children ) > 0 ) {
            $seriesArray = array (  );

            foreach ($children as $child) {
                $childrenIds[] = 'Filter[departments][]='.$child->id;

                foreach ($child->series (  ) as $series) {

                    if (!in_array($series->id,$seriesArray)) $seriesArray[] = $series->id;

                    if (!array_key_exists($series->id,$seriesArray)) $seriesArray[$series->id] = $series->name;

                }
            } // endforeach children as child
            $childrenString = implode( '&', $childrenIds );
            return ["series" => Series::whereIn ( 'id',$seriesArray )->get (  ),
                    "childrenString" => $childrenString];
        }
        return ["series" => $query->getModel()->series(  )];
    }

【问题讨论】:

  • ok 需要一些细节Series model => 表depcore_parts_products_departmentsdepcore_parts_products_series 对吗?接下来我们不能将department_id 添加到系列模型中,这样它就不会产生问题(不确定您的数据库架构)但我们可以使用它来避免参考链,系列可以直接指向部门而不是通过产品。
  • 这只是我的观点,可能是您有其他用例,如果您有其他用例并且上述评论没有帮助,那么我们可以写raw sql 让我们知道我们可以准备raw sql
  • 我试图避免通过部门系列复制多对多关系。因此,就像您指出的那样,它们是中间表。现在我想保持结构不变,并使用原始 sql 从产品中获取系列的 id。
  • 好的,我会尝试将您的问题复制到我的实例中,然后尝试为此构建原始查询
  • 我添加了一个原始的 sql 语句,它应该可以正常工作 - 现在我在将其转换为精简的活动记录实现时遇到了问题。

标签: mysql activerecord octobercms octobercms-plugins


【解决方案1】:

您可以利用原始查询并将fetched id 转换为模型,您可以编写此代码。

$sql = 'SELECT DISTINCT series_id FROM depcore_parts_products_series WHERE product_id IN (SELECT DISTINCT product_id FROM depcore_parts_products_departments WHERE department_id = :dep_id');

$data = ['dep_id' => 2];
$query = \DB::select($sql, $data);
$data = $query;

foreach ($data as $model) {
    $ids[] = $model->series_id;
}

$returnData = Series::whereIn('id',$ids)->get();
// dd($returnData);

return $returnData;

如果您发现任何困难,请发表评论。

更新

我的部门模型

use \October\Rain\Database\Traits\SimpleTree;

public $belongsTo = [
    'parent'    => ['HardikSatasiya\StackDemo\Models\Departments', 'key' => 'parent_id'],
];

public $hasMany = [
    'children'    => ['HardikSatasiya\StackDemo\Models\Departments', 'key' => 'parent_id'],
];

public function series() {

    $sql = 'SELECT DISTINCT series_id FROM hardiksatasiya_stackdemo_product_series WHERE product_id IN (SELECT DISTINCT product_id FROM hardiksatasiya_stackdemo_department_product WHERE department_id = :dep_id)';

    $data = ['dep_id' =>  $this->id];
    $query = \DB::select($sql, $data);
    $data = $query;
    foreach ($data as $model) {
        $ids[] = $model->series_id;
    }

    $returnData = Series::whereIn('id',$ids)->get();
    // dd($returnData);

    return $returnData;
}

public function scopeDepartmentSeries( $query ) {
    $children = $query->getModel()->getChildren();
    //dd($children);
    if ( count( $children ) > 0 ) {
        $seriesArray = array (  );

        foreach ($children as $child) {
            $childrenIds[] = 'Filter[departments][]='.$child->id;

            foreach ($child->series (  ) as $series) {

                if (!in_array($series->id,$seriesArray)) $seriesArray[] = $series->id;

                // if (!array_key_exists($series->id,$seriesArray)) $seriesArray[$series->id] = $series->name;

            }
        } // endforeach children as child
        $childrenString = implode( '&', $childrenIds );
        return ["series" => Series::whereIn ( 'id',$seriesArray )->get (  ),
                "childrenString" => $childrenString];
    }
    return ["series" => $query->getModel()->series(  )];
}

page code section

function onInit() {
    $departmentModel = \HardikSatasiya\StackDemo\Models\Departments::find(1);
    //dd($departmentModel->getChildren());
    $this['departmentModel'] = $departmentModel;
}

我正在使用您提供的html/partial它似乎在这里工作

【讨论】:

  • 这还是相当不错的,但我必须进行更改(第二次编辑),因为当使用刷新方法时,函数停止并且在视图中消失的项目。
  • 当然。已添加。
  • 谢谢,您使用的是departmentModel.departmentSeries.series,对吗?或者你不认为它应该是departmentModel.series,就像我的开发机器departmentModel.series 工作一样,如果它是departmentModel.departmentSeries.series,那么请需要关于departmentSeries 的一些细节。很抱歉询问,但需要详细信息来解决问题:(
  • 是的,部门是一个简单的树形结构。不要担心问我很高兴你的帮助,老实说,在我回家的路上我注意到我的编辑缺少你问的功能:)
  • 好的,谢谢,我会添加这些东西并测试它,哈哈你的问题是 1 英里长的办公室到家的距离.. :)
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2016-01-22
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多