我在一个项目中有一个包含两个大表的数据库," terminosnoticia"有4亿行和#34; noticia" 3百万。我有一个问题,我想打造更轻(它从10s到400s):
SELECT noticia_id, termino_id
FROM noticia
LEFT JOIN terminosnoticia on terminosnoticia.noticia_id=noticia.id AND termino_id IN (7818,12345)
WHERE noticia.fecha BETWEEN '2016-09-16 00:00' AND '2016-09-16 10:00'
AND noticia_id is not null AND termino_id is not null;`
我必须探索的唯一可行的解决方案是对数据库进行非规范化以包含“'”。大表中的字段,但是,这将乘以索引大小。
解释计划:
+----+-------------+-----------------+--------+-----------------------+------------+---------+-----------------------------------------+-------+-------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+-------------+-----------------+--------+-----------------------+------------+---------+-----------------------------------------+-------+-------------+
| 1 | SIMPLE | terminosnoticia | ref | noticia_id,termino_id | termino_id | 4 | const | 58480 | Using where |
| 1 | SIMPLE | noticia | eq_ref | PRIMARY,fecha | PRIMARY | 4 | db_resumenes.terminosnoticia.noticia_id | 1 | Using where |
+----+-------------+-----------------+--------+-----------------------+------------+---------+-----------------------------------------+-------+-------------+
按照建议更改查询并创建索引,解释计划现在是:
+----+-------------+-------+--------+-------------------------------------------+---------------------+---------+---------------------------+-------+-------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+-------------+-------+--------+-------------------------------------------+---------------------+---------+---------------------------+-------+-------------+
| 1 | SIMPLE | T | ref | noticia_id,termino_id,terminosnoticia_cpx | terminosnoticia_cpx | 4 | const | 60600 | Using index |
| 1 | SIMPLE | N | eq_ref | PRIMARY,fecha | PRIMARY | 4 | db_resumenes.T.noticia_id | 1 | Using where |
+----+-------------+-------+--------+-------------------------------------------+---------------------+---------+---------------------------+-------+-------------+
但执行时间并没有太大变化......
有什么想法吗?
答案 0 :(得分:4)
正如Strawberry所指出的那样,在你的where子句中使用“AND”表示NOT NULL 与常规INNER JOIN相同,可以缩减为。
SELECT
N.id as noticia_id,
T.termino_id
FROM
noticia N USING INDEX (fecha)
JOIN terminosnoticia T
on N.id = T.noticia_id
AND T.termino_id IN (7818,12345)
WHERE
N.fecha BETWEEN '2016-09-16 00:00' AND '2016-09-16 10:00'
现在,应用了所述和别名,我建议将以下覆盖索引作为
table index
Noticia ( fecha, id )
terminosnoticia ( noticia_id, termino_id )
这样查询可以直接从索引中获取所有结果,而不必转到原始数据页来限定其他字段。
答案 1 :(得分:1)
假设noticia_id
是noticia
的主键,我会添加以下索引:
create index noticia_fecha_idx on noticia(fecha);
create index terminosnoticia_id_noticia_idx on terminosnoticia(noticia_id);
再次尝试查询。
请包含查询的当前执行计划。它可能有助于你解决这个问题。
答案 2 :(得分:0)
试试这个:
SELECT tbl1.noticia_id, tbl1.termino_id FROM
( SELECT FROM terminosnoticia WHERE
terminosnoticia.termino_id IN (7818,12345)
AND terminosnoticia.noticia_id is not null
) tbl1 INNER JOIN
( SELECT id FROM noticia
WHERE noticia.fecha
BETWEEN '2016-09-16 00:00' AND '2016-09-16 10:00'
) tbl2 ON tbl1.id=tbl2.noticia.id
答案 3 :(得分:0)
我们假设noticia_id
和termino_id
是terminosnoticia
表中的列。 (如果所有列引用都使用表名或短表别名限定,我们就不必猜测。)
为什么这是外部联接? WHERE子句中的谓词将从terminosnoticia
中排除具有NULL值的行。这将否定"外在"外部"加入。
如果我们将其写为内连接,那么WHERE子句中的那些谓词就是多余的。我们已经知道noticia_id
不会为NULL(如果它满足ON子句中的等式谓词)。与termino_id
相同,如果它等于IN列表中的值,则不会为NULL。
我相信这个查询会返回一个等价的结果:
SELECT t.noticia_id
, t.termino_id
FROM noticia n
JOIN terminosnoticia t
ON t.noticia_id = n.id
AND t.termino_id IN (7818,12345)
WHERE n.fecha BETWEEN '2016-09-16 00:00' AND '2016-09-16 10:00'
现在剩下的就是弄清楚是否存在任何隐式数据类型转换。
我们没有看到termino_id
的数据类型。所以我们不知道这是否被定义为数字。如果不是,这是个坏消息,因为MySQL必须为表格中的每一行执行数字转换,因此它可以与数字文字进行比较。
我们没有看到noticia_id
的数据类型,以及它是否与要与之比较的列的数据类型匹配,来自id
的{{1}}列} table。
我们也没有看到noticia
的数据类型。基于谓词之间的字符串文字,看起来它可能是DATETIME或TIMESTAMP。但这只是猜测。我们不知道,因为我们没有可用的表格定义。
一旦我们确认没有任何隐含的数据类型转换会让我们感到不舒服......
对于使用内连接的查询(如上所述),合理性能的最佳镜头可能是MySQL有效使用覆盖索引。 (覆盖索引允许MySQL直接从索引块中满足查询,而无需在基础表中查找页面。)
正如DRApp的回答已经说明的那样,对于这个特定的查询,覆盖索引的最佳候选者将是:
fecha
具有相同前导列的索引也适用,并且会使这些索引冗余。
添加这些索引会使其他索引变得多余。
第一个索引对 ... ON noticia (fecha, id)
... ON terminosnoticia (noticia_id, termino_id)
是多余的。假设索引不强制执行UNIQUE约束,则可以删除它。任何有效使用该索引的查询都可以使用新索引,因为... ON noticia (fecha)
是新索引中的前导列。
同样,索引fecha
也是多余的。同样,假设它不是唯一索引,强制执行UNIQUE约束,那么也可以删除该索引。