LEFT JOIN与WHERE在第一个表的字段或第二个表的字段中

时间:2015-05-28 19:07:01

标签: mysql sql database performance database-design

SELECT COALESCE(tb2.number_new, tb1.number_original) as number
FROM
tb1
LEFT JOIN tb2 ON tb2.id = tb1.id
WHERE 
tb1.number_original = <PARAM> OR tb2.number_new = <PARAM>

以上查询生成全表扫描。如何优化它?

说明:

+----+-------------+-------+-------+----------------+----------------+---------+------+----------+----------------------------------------------------+
| id | select_type | table | type  | possible_keys  | key            | key_len | ref  | rows     | Extra                                              |
+----+-------------+-------+-------+----------------+----------------+---------+------+----------+----------------------------------------------------+
|  1 | SIMPLE      | tb1   | index | number_original| number_original| 5       | NULL | 11683843 | Using index                                        |
|  1 | SIMPLE      | tb2   | ALL   | PRIMARY        | NULL           | NULL    | NULL |        2 | Using where; Using join buffer (Block Nested Loop) |
+----+-------------+-------+-------+----------------+----------------+---------+------+----------+----------------------------------------------------+

我需要解决的问题是:

鉴于tb1是一个包含字段number_original的表,我想创建表tb2来存储此字段的新值,(我称之为number_new) 。我这样做是因为我无法改变number_original值,我不喜欢在表number_new上创建字段tb1,因为它在95%+的11mi记录中都是NULL。

2 个答案:

答案 0 :(得分:0)

您需要tb1.number_originaltb2.number_new的索引,也可能需要tb1.idtb2.id的索引。然后你需要MySQL来利用你的索引。我不知道是否会。

通常,当没有索引回答查询的搜索参数时,DBMS将扫描表。

答案 1 :(得分:0)

这是您的查询:

SELECT COALESCE(tb2.number_new, tb1.number_original) as number
FROM tb1 LEFT JOIN
     tb2
     ON tb2.id = tb1.id
WHERE  tb1.number_original = <PARAM> OR tb2.number_new = <PARAM>;

这是一个复杂的问题。首先,尝试两个索引tb1(number_original, id)tb2(number_new, id)

您可以将查询重写为:

SELECT COALESCE(tb2.number_new, tb1.number_original) as number
FROM tb1 LEFT JOIN
     tb2 
     ON tb2.id = tb1.id
WHERE tb1.number_original = <PARAM>
UNION
SELECT COALESCE(tb2.number_new, tb1.number_original) as number
FROM tb1 LEFT JOIN
     tb2 
     ON tb2.id = tb1.id
WHERE tb2.number_new = <PARAM>;

每个子查询都应使用其中一个索引。这确实会产生union的开销(重复删除),但这可能比全表扫描更好。