在 mysql 中应用多个联接时提高性能
Enhancing performance when applying multiple joins in mysql
我有 3 个 table:
Nutritions_facts table 静态 table 有所有营养 nutrition_id 和 nutrition_ename
Products_info table 包含产品信息,列为 product_id、product_ename、品牌
product_nutrition_facts
这是将产品及其营养成分与其价值联系起来的中介 table。结构为 product_id、nutrition_id、nutrition_value .. 每个产品可以有 1 行或更多行,具体取决于它所含的营养成分的数量。
这是一个真实的测试例子
Nutrition_facts table
nutrition_id |nutrition_ename
1 | caloreis
2 | fat
3 | sugar
4 | salt
Products_info table
product_id| product_ename | brand
1 | Nutella Hazelnut Cocoa | Nutella
2 | Nutella Jar | Nutella
product_nutrition_facts table
product_id | nutrition_id | nutrition_value
1 | 1 | 200
1 | 2 | 15
1 | 3 | 2
1 | 4 | 11
2 | 1 | 200
2 | 2 | 15
2 | 3 | 12
2 | 4 | 11
我需要查询return我的产品名称中糖的价值小于或等于 15,盐的价值小于或等于 140
我构建了一个 return 正确值的查询,但需要很长时间才能处理。有人可以建议修改以提高性能吗..
SELECT DISTINCT p.product_id, p.brand, p.e_name, p.image_low
FROM products_info p
JOIN product_nutrition_facts pn ON p.product_id = pn.product_id
WHERE p.brand = 'AL MARAI'
AND (
(
p.product_id
IN (
SELECT product_id
FROM product_nutrition_facts pn
WHERE pn.nutrition_id =3
AND pn.nutrition_value <=15
)
)
AND (
p.product_id
IN (
SELECT product_id
FROM product_nutrition_facts pn
WHERE pn.nutrition_id =4
AND pn.nutrition_value <=140
)
)
)
AND (
pn.nutrition_id =3
OR pn.nutrition_id =4
)
编辑
CREATE TABLE `products_info` (
`product_id` int(11) NOT NULL AUTO_INCREMENT,
`image_low` varchar(400) DEFAULT NULL,
`e_name` varchar(200) DEFAULT NULL,
PRIMARY KEY (`product_id`),
UNIQUE KEY `product_id_UNIQUE` (`product_id`)
) ENGINE=InnoDB AUTO_INCREMENT=249292 DEFAULT CHARSET=utf8
CREATE TABLE `product_nutrition_facts` (
`prod_nut_id` int(11) NOT NULL AUTO_INCREMENT,
`product_id` int(11) DEFAULT NULL,
`nutrition_id` int(11) DEFAULT NULL,
`nutrition_value` varchar(25) DEFAULT NULL,
`unit_id` int(11) DEFAULT NULL,
`parent` int(11) DEFAULT NULL,
`serving_size` varchar(145) DEFAULT NULL,
`serving_size_unit` int(11) DEFAULT NULL,
`no_nutrition_facts` int(11) NOT NULL,
`added_by` varchar(145) DEFAULT NULL,
`last_update` timestamp NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
`inserted_time` timestamp NOT NULL DEFAULT '0000-00-00 00:00:00',
`updated_by` varchar(150) NOT NULL,
PRIMARY KEY (`prod_nut_id`),
UNIQUE KEY `prod_nut_id_UNIQUE` (`prod_nut_id`),
KEY `nutrition_id_fk_idx` (`nutrition_id`),
KEY `unit_Fk_idx` (`unit_id`),
KEY `unit_fk1_idx` (`serving_size_unit`),
KEY `product_idFK` (`product_id`)
) ENGINE=InnoDB AUTO_INCREMENT=580809 DEFAULT CHARSET=utf8
CREATE TABLE `nutrition_facts` (
`nutrition_id` int(11) NOT NULL AUTO_INCREMENT,
`nutrition_aname` varchar(145) DEFAULT NULL,
`nutrition_ename` varchar(145) DEFAULT NULL,
`alternative_name` varchar(145) DEFAULT NULL,
`unit` varchar(8) NOT NULL,
`daily_value` int(11) NOT NULL,
`nut_order` int(2) NOT NULL,
`is_child` int(1) NOT NULL,
`last_update` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
PRIMARY KEY (`nutrition_id`)
) ENGINE=InnoDB AUTO_INCREMENT=53 DEFAULT CHARSET=utf8
尝试添加索引product_nutrition_facts (nutrition_id,nutrition_value,product_id)
、product_nutrition_facts (product_id,nutrition_id,nutrition_value)
、products_info (brand)
并执行查询
SELECT p.*
FROM products_info p
join product_nutrition_facts pn1 on
pn1.product_id=p.product_id
AND pn1.nutrition_id=3
AND pn1.nutrition_value<=15
join product_nutrition_facts pn2 on
pn2.product_id=p.product_id
AND pn2.nutrition_id=4
AND pn2.nutrition_value<=140
where
p.brand = 'AL MARAI'
IN ( SELECT ... )
-- 没有优化好;变成 JOIN
(如 Max 所建议的)
"Overnormalization" -- Nutrition_facts
可以消除;只需使用 nutrition_names 代替 nutrition_id。
我有 3 个 table:
Nutritions_facts table 静态 table 有所有营养 nutrition_id 和 nutrition_ename Products_info table 包含产品信息,列为 product_id、product_ename、品牌 product_nutrition_facts 这是将产品及其营养成分与其价值联系起来的中介 table。结构为 product_id、nutrition_id、nutrition_value .. 每个产品可以有 1 行或更多行,具体取决于它所含的营养成分的数量。 这是一个真实的测试例子
Nutrition_facts table
nutrition_id |nutrition_ename
1 | caloreis
2 | fat
3 | sugar
4 | salt
Products_info table
product_id| product_ename | brand
1 | Nutella Hazelnut Cocoa | Nutella
2 | Nutella Jar | Nutella
product_nutrition_facts table
product_id | nutrition_id | nutrition_value
1 | 1 | 200
1 | 2 | 15
1 | 3 | 2
1 | 4 | 11
2 | 1 | 200
2 | 2 | 15
2 | 3 | 12
2 | 4 | 11
我需要查询return我的产品名称中糖的价值小于或等于 15,盐的价值小于或等于 140
我构建了一个 return 正确值的查询,但需要很长时间才能处理。有人可以建议修改以提高性能吗..
SELECT DISTINCT p.product_id, p.brand, p.e_name, p.image_low
FROM products_info p
JOIN product_nutrition_facts pn ON p.product_id = pn.product_id
WHERE p.brand = 'AL MARAI'
AND (
(
p.product_id
IN (
SELECT product_id
FROM product_nutrition_facts pn
WHERE pn.nutrition_id =3
AND pn.nutrition_value <=15
)
)
AND (
p.product_id
IN (
SELECT product_id
FROM product_nutrition_facts pn
WHERE pn.nutrition_id =4
AND pn.nutrition_value <=140
)
)
)
AND (
pn.nutrition_id =3
OR pn.nutrition_id =4
)
编辑
CREATE TABLE `products_info` (
`product_id` int(11) NOT NULL AUTO_INCREMENT,
`image_low` varchar(400) DEFAULT NULL,
`e_name` varchar(200) DEFAULT NULL,
PRIMARY KEY (`product_id`),
UNIQUE KEY `product_id_UNIQUE` (`product_id`)
) ENGINE=InnoDB AUTO_INCREMENT=249292 DEFAULT CHARSET=utf8
CREATE TABLE `product_nutrition_facts` (
`prod_nut_id` int(11) NOT NULL AUTO_INCREMENT,
`product_id` int(11) DEFAULT NULL,
`nutrition_id` int(11) DEFAULT NULL,
`nutrition_value` varchar(25) DEFAULT NULL,
`unit_id` int(11) DEFAULT NULL,
`parent` int(11) DEFAULT NULL,
`serving_size` varchar(145) DEFAULT NULL,
`serving_size_unit` int(11) DEFAULT NULL,
`no_nutrition_facts` int(11) NOT NULL,
`added_by` varchar(145) DEFAULT NULL,
`last_update` timestamp NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
`inserted_time` timestamp NOT NULL DEFAULT '0000-00-00 00:00:00',
`updated_by` varchar(150) NOT NULL,
PRIMARY KEY (`prod_nut_id`),
UNIQUE KEY `prod_nut_id_UNIQUE` (`prod_nut_id`),
KEY `nutrition_id_fk_idx` (`nutrition_id`),
KEY `unit_Fk_idx` (`unit_id`),
KEY `unit_fk1_idx` (`serving_size_unit`),
KEY `product_idFK` (`product_id`)
) ENGINE=InnoDB AUTO_INCREMENT=580809 DEFAULT CHARSET=utf8
CREATE TABLE `nutrition_facts` (
`nutrition_id` int(11) NOT NULL AUTO_INCREMENT,
`nutrition_aname` varchar(145) DEFAULT NULL,
`nutrition_ename` varchar(145) DEFAULT NULL,
`alternative_name` varchar(145) DEFAULT NULL,
`unit` varchar(8) NOT NULL,
`daily_value` int(11) NOT NULL,
`nut_order` int(2) NOT NULL,
`is_child` int(1) NOT NULL,
`last_update` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
PRIMARY KEY (`nutrition_id`)
) ENGINE=InnoDB AUTO_INCREMENT=53 DEFAULT CHARSET=utf8
尝试添加索引product_nutrition_facts (nutrition_id,nutrition_value,product_id)
、product_nutrition_facts (product_id,nutrition_id,nutrition_value)
、products_info (brand)
并执行查询
SELECT p.*
FROM products_info p
join product_nutrition_facts pn1 on
pn1.product_id=p.product_id
AND pn1.nutrition_id=3
AND pn1.nutrition_value<=15
join product_nutrition_facts pn2 on
pn2.product_id=p.product_id
AND pn2.nutrition_id=4
AND pn2.nutrition_value<=140
where
p.brand = 'AL MARAI'
IN ( SELECT ... )
-- 没有优化好;变成 JOIN
(如 Max 所建议的)
"Overnormalization" -- Nutrition_facts
可以消除;只需使用 nutrition_names 代替 nutrition_id。