【发布时间】:2020-03-04 19:31:11
【问题描述】:
我有 4 个表:地点、品牌、类别、位置。
关系是 Places belongsTo Brands 和 Places ManytoMany with Categories and Locations。
我想获得具有特定类别和位置的搜索结果地点,但每个品牌只显示 1 个地点。
表格信息
places 表包含大约 100k+ 行
place_category 数据透视表包含 650k+ 行,place_category.place_id 和 places.brand_id 列已编入索引
place_locations 数据透视表包含大约 550k+ 行,place_location.place_id 和 place_location.location_id 列被索引
到目前为止我得到的查询
Place::join('place_location', function ($join) use ($city) {
$join->on('place_location.place_id', '=', 'places.id')
->where('place_location.location_id', '=', $city->id);
})
->join('place_category', function ($join) {
$join->on('place_category.place_id', '=', 'places.id')
->where('place_category.category_id', '=', $category->id);
})
->groupBy('places.brand_id')
->take(5)
->get();
groupBy 导致查询速度慢,查询时间约为 2 秒。
解释结果如下所示
id | select_type | table | possible_key | key | key_len | ref | rows | Extra
1 | SIMPLE | places | PRIMARY | brand_id | 4 | NULL | 50 | Using where
1 | SIMPLE | place_location | place_id,place_location | place_location | 4 | const,db.places.id | 1 | Using index
1 | SIMPLE | place_category | place_category | place_category | 4 | db.places.id,const | 1 | Using where; Using index
原始 Mysql 查询如下所示
select
`places`.`id`,
`places`.`name`,
`places`.`display`,
`places`.`status_a`,
`places`.`status_b`,
`places`.`brand_id`,
`places`.`address`
from `places`
inner join `place_location`
on `place_location`.`place_id` = `places`.`id`
and `place_location`.`location_id` = 4047
inner join `place_category`
on `place_category`.`place_id` = `places`.`id`
and `place_category`.`category_id` = 102
where
`places`.`status_a` != 1
and `status_b` = 2
and `display` >= 5
group by `places`.`brand_id`
limit 4
显示创建表是这样的
CREATE TABLE `places` (
`id` int(10) unsigned NOT NULL AUTO_INCREMENT,
`user_id` int(11) unsigned DEFAULT NULL,
`name` varchar(255) COLLATE utf8_unicode_ci NOT NULL,
`desc` varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL,
`city_id` int(11) unsigned NOT NULL DEFAULT '102',
`state_id` int(11) unsigned NOT NULL DEFAULT '34',
`location_id` int(11) unsigned NOT NULL DEFAULT '15',
`landmark_id` int(10) unsigned NOT NULL DEFAULT '1',
`postcode` varchar(50) COLLATE utf8_unicode_ci DEFAULT NULL,
`country_id` int(4) unsigned NOT NULL,
`lat` varchar(50) COLLATE utf8_unicode_ci DEFAULT NULL,
`long` varchar(50) COLLATE utf8_unicode_ci DEFAULT NULL,
`phone` varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL,
`sec_phone` varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL,
`third_phone` varchar(50) COLLATE utf8_unicode_ci DEFAULT NULL,
`fourth_phone` varchar(50) COLLATE utf8_unicode_ci DEFAULT NULL,
`brand_id` int(10) NOT NULL DEFAULT '1',
`display` int(11) NOT NULL DEFAULT '0',
`view` int(10) unsigned NOT NULL DEFAULT '0',
`status_b` tinyint(3) unsigned NOT NULL DEFAULT '2',
`status_a` tinyint(4) NOT NULL DEFAULT '2',
`company_name` varchar(100) COLLATE utf8_unicode_ci DEFAULT NULL,
`slug` varchar(255) COLLATE utf8_unicode_ci NOT NULL,
`lock_status_id` tinyint(3) unsigned DEFAULT '1',
`created_at` timestamp NULL DEFAULT NULL,
`updated_at` timestamp NULL DEFAULT NULL,
PRIMARY KEY (`id`),
UNIQUE KEY `slug` (`slug`),
KEY `city_id` (`city_id`),
KEY `location_id` (`location_id`),
KEY `user_id` (`user_id`),
KEY `landmark_id` (`landmark_id`),
KEY `name` (`name`),
KEY `brand_id` (`brand_id`),
KEY `groupby_brandid` (`status_b`, `display`) USING BTREE
) ENGINE=InnoDB AUTO_INCREMENT=116070 DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci
CREATE TABLE `place_location` (
`id` int(10) unsigned NOT NULL AUTO_INCREMENT,
`location_id` int(10) NOT NULL,
`place_id` int(10) NOT NULL,
PRIMARY KEY (`id`),
KEY `place_location` (`place_id`,`location_id`) USING BTREE
) ENGINE=InnoDB AUTO_INCREMENT=564259 DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci
CREATE TABLE `place_category` (
`id` int(10) unsigned NOT NULL AUTO_INCREMENT,
`category_id` int(11) unsigned NOT NULL,
`place_id` int(11) unsigned NOT NULL,
`branch_id` int(11) unsigned NOT NULL,
`created_at` timestamp NULL DEFAULT NULL,
`updated_at` timestamp NULL DEFAULT NULL,
PRIMARY KEY (`id`),
KEY `place_id` (`place_id`),
KEY `place_category` (`category_id`,`place_id`)
) ENGINE=InnoDB AUTO_INCREMENT=905384 DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci
知道如何改进查询吗?错误的索引?还是错误的查询?
【问题讨论】:
-
作为一个练习,你能否也包括你当前从你的 Laravel 代码运行的原始 MySQL 查询?仅通过查看您的 PHP 代码就很难了解该查询是什么。
-
刚刚添加了原始 MySQL
-
place_location 上的索引是 2 个键的索引还是 1 个键的 2 个索引?
-
它是一个有2列的索引,索引的列顺序是place_id和location_id列。我认为由 groupBY 引起的问题
-
注意(与性能无关)按
brand_id分组并且所有其他列未用聚合函数包装很可能会返回不确定的结果(我怀疑产品表中的所有列在功能上都依赖于brand_id ) 更多:Group by clause in mySQL and postgreSQL, why the error in postgreSQL?
标签: mysql sql laravel group-by sql-execution-plan