我试图找出我应该对我的查询和/或我的表格结构做些什么,以改进查询以获得在1秒内运行的畅销书。
以下是我正在谈论的问题:
SELECT pr.id_prod, MAX(pr.stock) AS stock, MAX(pr.dt_add) AS dt_add, SUM(od.quantity) AS quantity
FROM orders AS o
INNER JOIN orders_details AS od ON od.id_order = o.id_order
INNER JOIN products_references AS pr ON pr.id_prod_ref = od.id_prod_ref
INNER JOIN products AS p ON p.id_prod = pr.id_prod
WHERE o.id_order_status > 11
AND pr.active = 1
GROUP BY p.id_prod
ORDER BY quantity
LIMIT 10
如果我使用GROUP BY p.id_prod
代替GROUP BY pr.id_prod
并删除ORDER BY
,则查询将在0.07秒内运行。
是EXPLAIN表好吗?
id select_type table type possible_keys key key_len ref rows Extra
1 SIMPLE o range PRIMARY,id_order_status id_order_status 1 75940 Using where; Using index; Using temporary; Using filesort
1 SIMPLE od ref id_order,id_prod_ref id_order 4 dbname.o.id_order 1
1 SIMPLE pr eq_ref PRIMARY,id_prod PRIMARY 4 dbname.od.id_prod_ref 1 Using where
1 SIMPLE p eq_ref PRIMARY,name_url,id_brand,name PRIMARY 4 dbname.pr.id_prod 1 Using index
这是没有ORDER BY的EXPLAIN
id select_type table type possible_keys key key_len ref rows Extra
1 SIMPLE p index PRIMARY,name_url,id_brand,name PRIMARY 4 1 Using index
1 SIMPLE pr ref PRIMARY,id_prod id_prod 4 dbname.p.id_prod 2 Using where
1 SIMPLE od ref id_order,id_prod_ref id_prod_ref 4 dbname.pr.id_prod_ref 67
1 SIMPLE o eq_ref PRIMARY,id_order_status PRIMARY 4 dbname.od.id_order 1 Using where
这是表结构
CREATE TABLE `orders` (
`id_order` int(10) unsigned NOT NULL AUTO_INCREMENT,
`id_dir` int(10) unsigned DEFAULT NULL,
`id_status` tinyint(3) unsigned NOT NULL DEFAULT '11',
PRIMARY KEY (`id_order`),
KEY `id_dir` (`id_dir`),
KEY `id_status` (`id_status`)
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci;
CREATE TABLE `orders_details` (
`id_order_det` int(10) unsigned NOT NULL AUTO_INCREMENT,
`id_order` int(10) unsigned NOT NULL,
`id_prod_ref` int(10) unsigned NOT NULL,
`quantity` smallint(5) unsigned NOT NULL DEFAULT '1',
PRIMARY KEY (`id_order_det`),
UNIQUE KEY `id_order` (`id_order`,`id_prod_ref`) USING BTREE,
KEY `id_prod_ref` (`id_prod_ref`)
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci;
CREATE TABLE `products` (
`id_prod` int(10) unsigned NOT NULL AUTO_INCREMENT,
`name` varchar(60) COLLATE utf8_unicode_ci NOT NULL,
PRIMARY KEY (`id_prod`),
FULLTEXT KEY `name` (`name`)
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci;
CREATE TABLE `products_references` (
`id_prod_ref` int(10) unsigned NOT NULL AUTO_INCREMENT,
`id_prod` int(10) unsigned NOT NULL,
`stock` smallint(6) NOT NULL DEFAULT '0',
`dt_add` datetime DEFAULT NULL,
`active` tinyint(1) NOT NULL DEFAULT 0,
PRIMARY KEY (`id_prod_ref`),
KEY `id_prod` (`id_prod`)
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci;
我还尝试给你表关系(ON UPDATE,ON DELETE CASCADE,......)但是没有设法导出它。但我现在不认为它至关重要!
答案 0 :(得分:0)
尝试按顺序使用别名,而不是表
中的值并使用group by作为select中的值(对于join来说是相同的,因为内部联接的值相等,并且pr的值不会为select结果重新执行)
SELECT p.id_prod, p.name, SUM(od.quantity) AS quantity
FROM orders AS o
INNER JOIN orders_details AS od ON od.id_order = o.id_order
INNER JOIN products_references AS pr ON pr.id_prod_ref = od.id_prod_ref
INNER JOIN products AS p ON p.id_prod = pr.id_prod
WHERE pr.active = 1
GROUP BY p.id_prod
ORDER BY quantity
LIMIT 10
不要忘记在连接列上使用适当的索引
答案 1 :(得分:0)
(在OP添加更多信息后重写。)
SELECT pr.id_prod,
MAX(pr.stock) AS max_stock,
MAX(pr.dt_add) AS max_dt_add
SUM(od.quantity) AS sum_quantity
FROM orders AS o
INNER JOIN orders_details AS od
ON od.id_order = o.id_order
INNER JOIN products_references AS pr
ON pr.id_prod_ref = od.id_prod_ref
WHERE o.id_order_status > 11
AND pr.active = 1
GROUP BY pr.id_prod
ORDER BY sum_quantity
LIMIT 10
请注意,p
已删除为无关紧要。
将SUM()
与JOIN
一起使用时要小心GROUP BY
- 您可能会获得错误的,夸大的价值。
一张桌子的改进:
CREATE TABLE `orders_details` (
`id_order` int(10) unsigned NOT NULL,
`id_prod_ref` int(10) unsigned NOT NULL,
`quantity` smallint(5) unsigned NOT NULL DEFAULT '1',
PRIMARY KEY (`id_order`,`id_prod_ref`),
INDEX (id_prod_ref, id_order)
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci;
这就是为什么:od
听起来像很多:很多映射表。有关提高效果的提示,请参阅here。
GROUP BY
通常涉及一种排序。 ORDER BY
,当它与GROUP BY
不完全相同时,肯定需要另一种。
删除ORDER BY
允许查询返回任何10行而不进行排序。 (这可以解释时间差异。)
请注意别名sum_quantity
,以避免列 quantity
与您的别名 quantity
之间存在歧义。
解释说明
1 SIMPLE o range id_order_status 1 75940 Using where; Using index; Using temporary; Using filesort
1 SIMPLE od ref id_order 4 o.id_order 1
1 SIMPLE pr eq_ref PRIMARY 4 od.id_prod_ref 1 Using where
1 SIMPLE p eq_ref PRIMARY 4 pr.id_prod 1 Using index
o
无法使用数据("使用索引")但会扫描包含id_order_status
的{{1}}索引。注意:(id_status, id_order)
列含蓄地添加到任何辅助密钥。PRIMARY KEY
的覆盖范围可能会找到1行,可能会找到0或超过1(" ref")。od
和pr
时,最多可获得1行。p
进行少量过滤(pr
),但直到active=1
的第三行。并且没有索引对此过滤有用。这可以通过复合索引EXPLAIN
进行改进,但只能稍微改进一下。只有5-10%被过滤掉,这不会有太大帮助。(active, id_prod_ref)
和过滤后,会有两个临时表和排序,一个用于JOINing
,一个用于GROUP BY
。如果没有ORDER BY,ORDER BY
会显示不同的顺序似乎更好。而tmp&排序消失了。
EXPLAIN
1 SIMPLE p index PRIMARY 4 1 Using index
1 SIMPLE pr ref id_prod 4 p.id_prod 2 Using where
1 SIMPLE od ref id_prod_ref 4 pr.id_prod_ref 67
1 SIMPLE o eq_ref PRIMARY 4 dbne.od.id_order 1 Using where
似乎只有1
行,对吗?因此,在某种程度上,访问此表时无关紧要。当你有多个"产品"所有这些分析都可能改变!p
是"集群"与数据。PRIMARY KEY
行?也许优化器意识到不需要pr
?GROUP BY
时,估计" 67"每个p + pr组合将需要行。od
,因此无需排序,任何 10行都可以投放。