【问题标题】:How to optimize mysql query using php/mysql eager loading or similar approach如何使用 php/mysql 急切加载或类似方法优化 mysql 查询
【发布时间】:2015-02-12 20:13:37
【问题描述】:

我有 2 张桌子。 1 - “cpv”; 2 - “club_offer_cpv”。

在“cpv”表中,我存储了 CPV 代码、它们的 ID 和名称。您可以看到 CPV 代码here。 CPV 代码共有 6 个级别,您可以通过访问提供的链接查看。

在 CPV 表中有字段:“id”和“name”。

以下是一些一级 CPV 的示例(我们可以称它们为根 CPV):

id:03000000
名称:农、农、渔、林及相关产品

id: 09000000
名称:石油产品、燃料、电力和其他能源

有 45 个根 CPV,它们有自己的子节点。从我提供给您的链接中可以看出,他们 ID 的层次结构非常严格。例如,根 CPV 03000000 具有二级子级:

03100000 : 农产品和园艺产品
03200000 : 谷物、土豆、蔬菜、水果和坚果
03300000 : 农业、狩猎和渔业产品
03400000 : 林业和伐木产品

现在,对于每个 CPV,都有一家公司在该领域开展业务(公司正在为该领域提供报价),例如,

03100000 : 农产品和园艺产品

很少有公司的业务是创造食物,这就是他们与这个 cpv 相关联的原因。这些公司的报价存储在不同的表“company_offer”中,但这现在并不重要。重要的是有一个表“company_offer_cpv”连接 company_offer 和 cpv 表。

“company_offer_cpv”表有这些字段:

“id”、“company_offer_id”、“cpv_id”。这就是我可以将公司与其领域的 cpv 代码联系起来的方法,因为他们可以分配有许多代码。

我想做的是为每个 CPV 组计算 company_offer_cpv 表中使用的所有 CPV,并在组旁边显示这些数字(当我说组时,我的意思是 root cpv)名称。像这样:

03000000 : 农业、农业、渔业、林业及相关产品 : 240
09000000 : 石油产品、燃料、电力和其他能源 : 130
14000000:采矿、基本金属及相关产品:72

等等……

我用的是yii2 php框架,不过如果你不使用也没关系,你可以帮我用mysql/php代码。

要计算表 company_offer_cpv 中使用的所有 CPV 的数量,并按上述方式显示它们,我使用以下代码:

$total = ClubOfferCpv::find()->groupBy( ['LEFT(cpv_id, 2)'] )
                             ->select( ['LEFT(cpv_id, 2) AS cpv_id', 'COUNT(cpv_id) AS count'])
                             ->all();

foreach ($total as $data) 
{
    $id = $data->cpv_id."000000";

    $cpvName = Cpv::find()->select('name')->where(['id' => $id])->one();

    echo $cpvName->name. " " . $data->count;
    echo "<br>";
}

但是我的代码中有一个缺陷(或更多:))。如果你看一下 foreach 循环,我正在执行这行代码来获取所有根 cpv 名称,这样我就可以在计数旁边显示它们:

$cpvName = Cpv::find()->select('name')->where(['id' => $id])->one();

由于有 45 个根 CPV,此行将导致执行 45 个查询。我需要总共进行 1 或 2 个查询,但我不知道如何。我知道 Yii2 提供了Eager Loading 但我不知道如何使用它。有谁知道我可以如何使用 yii2 或简单的 sql/php 来解决这个问题?

这是第一行代码执行的sql:

SELECT LEFT(cpv_id, 2) AS cpv_id, COUNT(cpv_id) AS count FROM `club_offer_cpv` GROUP BY LEFT(cpv_id, 2)

这是由 foreach 中的行执行的 SQL 之一:

SELECT `name` FROM `cpv` WHERE `id`='50000000'

提前谢谢你


这是为了很好地回应 JC Sama 的帮助而进行的更新:

如果我按照你所说的那样尝试使用 yii2 预加载,我不会得到任何结果,因为我需要从我的 company_offer_cpv 表中删除 cpv_id。以下是 eager 执行的查询:

SELECT LEFT(cpv_id, 2) AS cpv_id, COUNT(cpv_id) AS count FROM `club_offer_cpv` GROUP BY LEFT(cpv_id, 2) ;   

第二个:

SELECT * FROM `cpv` WHERE `id` IN ('03', '09', '14', '15', '16', '18', '19', '22', '24', '30', '31', '32', '33', '34', '35', '37', '38', '39', '41', '42', '43', '44', '45', '48', '50', '51', '55', '60', '63', '64', '65', '66', '70', '71', '72', '73', '75', '76', '77', '79', '80', '85', '90', '92', '98')

非常感谢 JC 萨玛!

这里是完整的解决方案:

$total = ClubOfferCpv::find()->groupBy( ['LEFT(cpv_id, 2)'] )
                             ->select( ['LEFT(cpv_id, 2) AS cpv', 'CONCAT(LEFT(cpv_id, 2), "000000") AS cpv_id', 'COUNT(cpv_id) AS count'])
                             ->with('cpv')
                             ->all();

foreach ($total as $data) 
{
    echo $data->cpv->name. " " . $data->count;
    echo "<br>";
}

【问题讨论】:

    标签: php mysql sql yii2 eager-loading


    【解决方案1】:

    我对 Yii 不熟悉,但根据文档,你可以试试这样的:

    $total = ClubOfferCpv::find()->groupBy( ['LEFT(cpv_id, 2)'] )
                                 ->select( ['LEFT(cpv_id, 2) AS cpv', 'CONCAT(LEFT(cpv_id, 2), "000000") AS cpv_id', 'COUNT(cpv_id) AS count'])
                                 ->with('cpv')
                                 ->all();
    

    或者您可以像这样在第二个查询中使用WHERE IN() 语句,因为使用 Eager loading 的想法是相同的:

    $ids = array();
    foreach ($total as $data) 
    {
        $id[] = $data->cpv_id."000000";
    }
    

    然后:

    // I'm not sure about WhereIn() Yii stuff :)
    $cpvNames = Cpv::find()->select('name, id')->where(['id' => $ids])->all();
    // Create an associative array, not sure how it's done with Yii
    $cpvNames = CHtml::listData( $cpvNames, 'id' , 'name'); 
    

    最后:

    foreach ($total as $data) {
    
        $id = $data->cpv_id."000000";
        echo $cpvNames[$id]. " " . $data->count;
        echo "<br>";
    }
    

    回顾: 您最终只会得到 2 个查询:

    SELECT LEFT(cpv_id, 2) AS cpv_id, COUNT(cpv_id) AS count FROM `club_offer_cpv` GROUP BY LEFT(cpv_id, 2)
    

    第二个使用where in 而不是equal

    SELECT `name` FROM `cpv` WHERE `id` in ('50000000', 800000, ...)
    

    【讨论】:

    • 如果我按照您所说的那样尝试使用 yii2 急切加载,我不会得到任何结果,因为我需要修剪我的 company_offer_cpv 表中的 cpv_id。以下是 eager 执行的查询: SELECT LEFT(cpv_id, 2) AS cpv_id, COUNT(cpv_id) AS count FROM club_offer_cpv GROUP BY LEFT(cpv_id, 2) ;第二个:SELECT * FROM cpv WHERE id IN ('03', '09', '14', '15', '16', '18', '19', '22', '24' ,'30','31','32','33','34','35','37','38','39','41','42','43',' 44'、'45'、'48'、'50'、'51'、'55'、'60'、'63'、'64'、'65'、'66'、'70'、'71' , '72', '73', '75', '76', '77', '79', '80', '85', '90', '92', '98')
    • 首先使用 yii2 急切加载,因为第二次显示来自 yii1 的代码,而我不知道如何使用它。我已经编辑了我的帖子,请阅读最后。
    • 思路是一样的,就是在第二个查询中用ÌN`关键字代替=,可以翻译成Yii代码。无论如何,它正在工作,但你必须删除 LEFT 关键字
    • 如果我删除 LEFT 我仍然没有得到结果,因为这是查询: SELECT * FROM cpv WHERE id IN ('03441000', '09100000', '14212310', ' 15861100','16400000','18000000','19212200','22460000','24453000','30200000','30200000','31625300','32323500','32323500','33111500 , '38436710', '39251100', '41110000', '42961300', '43000000', '44112200', '45312200', '48100000', '50111100', '514300000', '6050300000', '6053 63710000','64121000','65210000','66112000','70123200','71317200','71317200','72212321','73431000 )
    • 比如没有父03441000,有03000000
    猜你喜欢
    • 1970-01-01
    • 2011-12-15
    • 2018-04-14
    • 2014-12-09
    • 1970-01-01
    • 2011-10-27
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多