正确的优化MySQL查询的方法

时间:2011-08-05 16:30:49

标签: mysql

我一直在努力寻找从多个表中选择的查询。我原来的查询速度非常慢(53秒)。从阅读起,我现在有理由相信我需要创建一个内部查询来限制迭代的数据。但是当我使用2个以上的表时,我不确定如何使用子查询的结果(内部查询)。下面是一些虚拟表:

+-------+---------------------+------------+
| tr_id | tr_datecreated      | tr_depart  |
+-------+---------------------+------------+
|     1 | 2011-07-31 00:00:00 | 2011-08-20 |
|     2 | 2011-08-01 00:00:00 | 2011-08-30 |
|     3 | 2011-08-02 00:00:00 | 2011-09-01 |
+-------+---------------------+------------+

+------+--------+---------+---------+
| p_id | p_trid | p_name  | p_lname |
+------+--------+---------+---------+
|    1 |      1 | Geoff   | Thingy  |
|    2 |      1 | Mildred | Thingy  |
|    3 |      1 | Garry   | Thingy  |
|    4 |      2 | Linda   | Doobrey |
|    5 |      2 | Kev     | Doobrey |
|    6 |      3 | John    | Wotsit  |
|    7 |      3 | Jill    | Wotsit  |
+------+--------+---------+---------+

+------+--------+----------+
| h_id | h_trid | h_dest   |
+------+--------+----------+
|    1 |      1 | France   |
|    2 |      1 | Spain    |
|    3 |      2 | Italy    |
|    4 |      3 | Portugal |
+------+--------+----------+

我想得到一个结果,如:

+-------+---------------------+------------+---------+---------+----------+
| tr_id | tr_datecreated      | tr_depart  | p_name  | p_lname | h_dest   |
+-------+---------------------+------------+---------+---------+----------+
|     1 | 2011-07-31 00:00:00 | 2011-08-20 | Geoff   | Thingy  | France   |
|     1 | 2011-07-31 00:00:00 | 2011-08-20 | Geoff   | Thingy  | Spain    |
|     1 | 2011-07-31 00:00:00 | 2011-08-20 | Mildred | Thingy  | France   |
|     1 | 2011-07-31 00:00:00 | 2011-08-20 | Mildred | Thingy  | Spain    |
|     1 | 2011-07-31 00:00:00 | 2011-08-20 | Garry   | Thingy  | France   |
|     1 | 2011-07-31 00:00:00 | 2011-08-20 | Garry   | Thingy  | Spain    |
|     2 | 2011-08-01 00:00:00 | 2011-08-30 | Linda   | Doobrey | Italy    |
|     2 | 2011-08-01 00:00:00 | 2011-08-30 | Kev     | Doobrey | Italy    |
|     3 | 2011-08-02 00:00:00 | 2011-09-01 | John    | Wotsit  | Portugal |
|     3 | 2011-08-02 00:00:00 | 2011-09-01 | Jill    | Wotsit  | Portugal |
+-------+---------------------+------------+---------+---------+----------+

我们为每个度假目的地的每个人获得一个单独的行。

我最初的努力是:

SELECT tr_id, tr_datecreated, tr_depart, p_name, p_lname, h_dest
FROM transaction, people, holiday
WHERE tr_id = p_trid
AND tr_id = h_trid
AND tr_datecreated >= "2010-12-12 00:00:00"
AND tr_datecreated <= "2012-12-12 00:00:00"

我认为这会产生大量的交叉连接,并且查询运行速度非常慢。

看到tr_id被多次引用我想做一个内部查询,这减少了与其他所有内容进行比较的行数。

所以内部查询部分将是:

SELECT tr_id WHERE tr_datecreated >= "2010-12-12 00:00:00" 
AND tr_datecreated <= "2012-12-12 00:00:00"

我如何创建我想要的表格,我希望将p_trid和h_trid与同一内部查询进行比较而不运行该内部查询两次(如果可能)?

在这种情况下,内部联接会有帮助吗? (我已经阅读但尚未完全吸收它。)

感谢这里的任何建议和意见。数据库很大,我需要高效。

修改

索引:

tr_id,h_id和p_id都是主键

EXPLAIN的结果

+----+-------------+--------------+--------+---------------+---------+---------+---------------------+------+--------------------------------+
| id | select_type | table        | type   | possible_keys | key     | key_len | ref                 | rows | Extra                          |
+----+-------------+--------------+--------+---------------+---------+---------+---------------------+------+--------------------------------+
|  1 | SIMPLE      | holiday      | ALL    | NULL          | NULL    | NULL    | NULL                |    4 |                                |
|  1 | SIMPLE      | people       | ALL    | NULL          | NULL    | NULL    | NULL                |    7 | Using where; Using join buffer |
|  1 | SIMPLE      | transactions | eq_ref | PRIMARY       | PRIMARY | 4       | db.people.p_trid |    1 | Using where                    |
+----+-------------+--------------+--------+---------------+---------+---------+---------------------+------+--------------------------------+

3 个答案:

答案 0 :(得分:1)

你试过加入吗?

SELECT tr.tr_id, tr.tr_datecreated, tr.tr_depart, p.p_name, p.p_lname, h.h_dest
FROM transaction tr
join people p on tr.tr_id = p.p_trid
join holiday h on tr.tr_id = h.h_trid  
WHERE tr_datecreated >= "2010-12-12 00:00:00"
AND tr_datecreated <= "2012-12-12 00:00

还没有测试过这个,但这是一般的想法。

答案 1 :(得分:1)

我认为这应该有效。如果有效,请告诉我。

总查询

SELECT t.id, t.date, t.depart, p.p_name, p.p_lname, h.h_dest 
FROM
(SELECT tr_id 'id', tr_datecreated 'date', tr_depart 'depart' FROM transaction 
WHERE DATE(tr_datecreated) BETWEEN DATE("2010-12-12 00:00:00") 
AND DATE("2012-12-12 00:00:00")) t
JOIN people p  ON t.id = p.p_trid 
JOIN holiday h ON t.id = h.h_trid;

内部查询

(SELECT tr_id 'id', tr_datecreated 'date', tr_depart 'depart' FROM transaction 
WHERE DATE(tr_datecreated) BETWEEN DATE("2010-12-12 00:00:00") 
AND DATE("2012-12-12 00:00:00"))

编辑:子查询说明

子查询从上面列出的日期范围中选择事务表中的id,创建日期和离开列。查询末尾右边的“t”允许您对内部查询进行别名,以便您可以使用上面的数据。此外,我在子查询中有'id''date''depart'的位置也是别名。它允许您使用这些值而无需键入完整的列名称。

希望这会有所帮助。

答案 2 :(得分:1)

我建议在people.p_trid和holiday.h_trid上添加一个索引。 EXPLAIN清楚地表明两个表都没有使用索引。

还要确保transactions.tr_id,people.p_trid和holiday.h_trid的数据类型相同。