joinWith
Yii2 code:
$query = MiniAppRisk::find()->joinWith(['country','text','riskType'])->orderBy(['risk_publish_date' => SORT_DESC]);
$dataProvider = new ActiveDataProvider([
'query' => $query,
'pagination' => [
'pageSize' => 10,
'page' =>0
],
]);
result sql1
:
SELECT `fmm_text_risk_t`.risk_id FROM `fmm_text_risk_t`
LEFT JOIN `fmm_text_risk_target_t` ON `fmm_text_risk_t`.`risk_id` = `fmm_text_risk_target_t`.`risk_id`
LEFT JOIN `country` ON `fmm_text_risk_target_t`.`country_id` = `country`.`id`
LEFT JOIN `fmm_text_t` ON `fmm_text_risk_t`.`text_id` = `fmm_text_t`.`text_id`
LEFT JOIN `fmm_text_risk_type_relation_t` ON `fmm_text_risk_t`.`risk_id` = `fmm_text_risk_type_relation_t`.`risk_id`
ORDER BY `risk_publish_date` DESC LIMIT 10
with
Yii2 code:
$query = MiniAppRisk::find()->with(['country','text','riskType'])->orderBy(['risk_publish_date' => SORT_DESC]);
$dataProvider = new ActiveDataProvider([
'query' => $query,
'pagination' => [
'pageSize' => 10,
'page' =>0
],
]);
result sql2
SELECT DISTINCT * FROM `fmm_text_risk_t` ORDER BY `risk_publish_date` DESC LIMIT 10 ;
SELECT * FROM `fmm_text_risk_target_t` WHERE `risk_id` IN (5563, 5562, 5561, 5560, 5559, 5558, 5557, 5556, 5555, 5554);
SELECT * FROM `country` WHERE `id` IN (7, 165, 1, 20);
SELECT * FROM `fmm_text_t` WHERE `text_id` IN (15272, 15271, 15270, 15269, 15268, 15267, 15266, 15265, 15264, 15263);
SELECT * FROM `fmm_text_risk_type_relation_t` WHERE `risk_id` IN (5563, 5562, 5561, 5560, 5559, 5558, 5557, 5556, 5555, 5554);
...more
It seems that the SQL generated by joinWith is less, and the efficiency is higher. However, be cautious when using joinWith for pagination. When you examine their execution results, you will notice that the number of entries per page is less than 10. This is due to the mechanism of the left join. In the case of a one-to-many relationship, it may result in data duplication, and the 'LIMIT 10' applies to the entire SQL limit, exacerbating the issue.So got these 'risk_id':
27680
27680
27900
27900
27900
30099
30099
30301
30301
30489
To address this issue, you can do the following:
$query = MiniAppRisk::find()->joinWith(['country','text','riskType'])->distinct();
SELECT DISTINCT `fmm_text_risk_t`.risk_id FROM `fmm_text_risk_t`
LEFT JOIN `fmm_text_risk_target_t` ON `fmm_text_risk_t`.`risk_id` = `fmm_text_risk_target_t`.`risk_id`
LEFT JOIN `country` ON `fmm_text_risk_target_t`.`country_id` = `country`.`id`
LEFT JOIN `fmm_text_t` ON `fmm_text_risk_t`.`text_id` = `fmm_text_t`.`text_id`
LEFT JOIN `fmm_text_risk_type_relation_t` ON `fmm_text_risk_t`.`risk_id` = `fmm_text_risk_type_relation_t`.`risk_id`
ORDER BY `risk_publish_date` DESC LIMIT 10
When using with
limiting is applied only to the primary table, thus avoiding the aforementioned issue.
joinWith
still results in 2 queries... for reasons I don't quite understand... I have always understood "the fewer SQL queries the better".joinWith
is therefore only useful if you are querying on a column in the related table, otherwise it's only a drawback. – Zootoxin