如何使用case语句优化选择查询?

时间:2019-06-20 18:54:50

标签: mysql sql indexing query-optimization

我有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;

3 个答案:

答案 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

我的某些部分可能不太正确;看看您是否可以使此公式有效。