我有3张桌子,超过1,000,000条记录。我的选择查询运行了几个小时。 如何优化呢?我是新手。
我尝试为name
添加索引,但仍然需要花费几个小时才能加载。
喜欢这个
ALTER TABLE table2 ADD INDEX(name);
也这样
CREATE INDEX INDEX1 table2(name);
SELECT MS.*, P.Counts FROM
(SELECT M.*,
TIMESTAMPDIFF(YEAR, M.date, CURDATE()) AS age,
CASE V.name
WHEN 'text' THEN M.name
WHEN V.name IS NULL THEN M.name
ELSE V.name
END col1
FROM table1 M
LEFT JOIN table2 V ON M.id=V.id) AS MS
LEFT JOIN
(select E.id, count(E.id) Counts
from table3 E
where E.field2 = 'value1'
group by E.id) AS P
ON MS.id=P.id;
Explain <above query>;
输出:
+----+-------------+------------+------------+-------+---------------------------------------------+------------------+---------+------------------------+---------+----------+-----------------------------------------------------------------+
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+-------------+------------+------------+-------+---------------------------------------------+------------------+---------+------------------------+---------+----------+-----------------------------------------------------------------+
| 1 | PRIMARY | M | NULL | ALL | NULL | NULL | NULL | NULL | 344763 | 100.00 | NULL |
| 1 | PRIMARY | <derived3> | NULL | ref | <auto_key0> | <auto_key0> | 8 | CP.M.id | 10 | 100.00 | NULL |
| 1 | PRIMARY | V | NULL | index | NULL | INDEX1 | 411 | NULL | 1411083 | 100.00 | Using where; Using index; Using join buffer (Block Nested Loop) |
| 3 | DERIVED | E | NULL | ref | PRIMARY,f2,f3 | f2| 43 | const | 966442 | 100.00 | Using index |
+----+-------------+------------+------------+-------+---------------------------------------------+------------------+---------+------------------------+---------+----------+-----------------------------------------------------------------+
我希望在不到1分钟的时间内得到结果。
为清楚起见,查询缩进。
SELECT MS.*, P.Counts
FROM (
SELECT M.*,
TIMESTAMPDIFF(YEAR, M.date, CURDATE()) AS age,
CASE V.name
WHEN 'text' THEN M.name
WHEN V.name IS NULL THEN M.name
ELSE V.name
END col1
FROM table1 M
LEFT JOIN table2 V ON M.id=V.id
) AS MS
LEFT JOIN (
select E.id, count(E.id) Counts
from table3 E
where E.field2 = 'value1'
group by E.id
) AS P ON MS.id=P.id;
答案 0 :(得分:1)
您的查询没有过滤谓词,因此它实际上是在检索所有行。那是来自table1
的1,000,000+行。然后将其与table2
,然后与另一个表表达式/派生表联接。
为什么您希望这个查询很快?像这样的大型查询通常会在晚上作为批处理运行。我认为该查询不是针对在线过程的,对吧?
也许您需要重新考虑该过程。您是否真的需要一次交互式地处理数百万行?用户会在网页上阅读一百万行吗?
答案 1 :(得分:0)
对于初学者,如果v.name为null或v.name!='text',则对于'col1'返回相同的结果。就是说,您可以在与table2联接并使用IFNULL函数时包括该额外条件。
如果您要按field2过滤table3,则可以在包含field2的表3上创建索引。
您还应该检查是否可以为这些表中的任何表添加其他过滤器,如果可以,则可以考虑使用存储过程来获取结果。
此外,我不明白为什么需要将第一个联接聚合到“ MS”中,因此可以轻松地将所有联接像这样进行:
SELECT
M.*,
TIMESTAMPDIFF(YEAR, M.date, CURDATE()) AS age,
IFNULL(V.name, M.name) as col1,
P.Counts
FROM table1 M
LEFT JOIN table2 V ON M.id=V.id AND V.name <> 'text'
LEFT JOIN
(SELECT
E.id,
COUNT(E.id) Counts
FROM table3 E
WHERE E.field2 = 'value1'
GROUP BY E.id) AS P ON M.id=P.id;
我还假设您在所有这三个表中的所有id字段都具有聚集索引,但是没有过滤器,如果您要处理数百万条记录,这将一直是一个繁重的查询。至少可以说,您正在对table1进行表扫描。
在您发表评论后,我还提供了这些附加信息。
我已经提到了聚集索引,但是根据有关索引here
的官方文档当您在表上定义PRIMARY KEY时,InnoDB会将其用作聚簇索引。因此,如果您已经定义了主键,则无需执行其他任何操作。 文档中也指出了您应该为所创建的每个表定义一个主键。
如果您没有主键。这是您要求的代码段。
ALTER TABLE table1 ADD CONSTRAINT pk_table1
PRIMARY KEY CLUSTERED (id);
注意::请记住,对于像您这样的具有数据色调的表,创建聚簇索引是一项重大操作。 在生产服务器上,如果没有计划,这不是您想要做的。此操作还将花费很长时间,并且在此过程中表将被锁定。
答案 2 :(得分:0)
子查询并非总是经过优化的。
我认为您可以将其压平,例如:
SELECT M.*, V.*,
TIMESTAMPDIFF(YEAR, M.date, CURDATE()) AS age,
CASE V.name WHEN 'text' THEN M.name
WHEN V.name IS NULL THEN M.name
ELSE V.name END col1,
( SELECT COUNT(*) FROM table3 WHERE field2 = 'value1' AND id = x.id
) AS Counts
FROM table1 AS M
LEFT JOIN table2 AS V ON M.id = V.id
我的某些部分可能不太正确;看看您是否可以使此公式有效。