为什么选择左连接+哪里慢得多

时间:2013-09-14 00:26:01

标签: mysql sql

我正在尝试优化需要很长时间才能处理的MySQL查询。想象一下,我们有两个表,一个用户表和一个购买表。两个表都有大约20,000行。

mysql> 
SELECT NOW(),u.id
    FROM users u
    LEFT JOIN purchases p
        ON p.user_id = u.id
    WHERE
        p.website_id = 1234
    ORDER BY u.total_paid DESC
    LIMIT 10;
+---------------------+-------+
| NOW()               | id    |
+---------------------+-------+
*snip*
+---------------------+-------+
10 rows in set (0.06 sec)

不是超级快但很漂亮。如果除了将u.id更改为u.*之外什么也不做更改,它会显着减慢:

mysql>
SELECT NOW(),u.*
    FROM users u
    LEFT JOIN purchases p
        ON p.user_id = u.id
    WHERE
        p.website_id = 1234
    ORDER BY u.total_paid DESC
    LIMIT 10;
+---------------------+-------+
*snip*
+---------------------+-------+
10 rows in set (0.37 sec)

在您说“嗯,您永远不应该使用select *”之前,请考虑它添加的字段越来越慢,即命名一半要选择的字段将导致查询执行〜0.20秒,用户表上没有字段大于varchar(255)

但是,如果我从相对简单的查询中获取ID,我只是:

mysql>
SELECT *
    FROM users
    WHERE id IN (*snip*);
+---------------------+-------+
*snip*
+---------------------+-------+
10 rows in set (0.01 sec)

所以我的两个查询:select u.idselect u.* where id in比我假设的类似查询更快。到底是什么?

更多信息:users表上有大约30个字段。同样,没有字段大于varchar(255)

更多信息:两个查询的EXPLAIN都是:

*************************** 1. row ***************************
           id: 1
  select_type: SIMPLE
        table: p
         type: ref
possible_keys: PRIMARY,user_id_index,website_id_index,website_user_id_index,website_created_index,website_type_created_index,website_type_index,purchase_user_id_type_index,user_id_website_id_index,website_id_user_id_index
          key: website_id_user_id_index
      key_len: 9
          ref: const
         rows: 9976
        Extra: Using where; Using index; Using temporary; Using filesort
*************************** 2. row ***************************
           id: 1
  select_type: SIMPLE
        table: u
         type: eq_ref
possible_keys: PRIMARY
          key: PRIMARY
      key_len: 8
          ref: database.p.user_id
         rows: 1
        Extra:

编辑可能是因为它正在使用临时/ filesort,它必须从用户中选择*,而不知道哪些行最终会出现在最终结果集中?所以它看起来似乎是一些微不足道的额外数据,但实际上它是选择一大块表格之间的区别?如果这是正确的,有任何建议吗?

1 个答案:

答案 0 :(得分:2)

首先,我想问/部分答案。你真正要求的是什么?您对purchase表有一个LEFT-JOIN,但是对于特定的“购买”网站ID有一个WHERE子句。这实质上是将查询带到INNER JOIN,并且仅返回那些从相关站点购买DID的用户。也就是说,我会将查询重写为

select 
      NOW(),
      u.id 
   from 
      purchases p
         JOIN users u 
            ON p.user_id = u.id
   where 
      p.website_id = 1234 
   order by 
      u.total_paid desc 
   limit 
      10;

假设您在(Website_ID)上有索引,这将首先从购买开始并加入用户,但仅限于在网站1234上购买。这也可能给出错误答案,如果一个用户从同一个网站,他们是最大的买家之一...他们的ID可能会出现多次。为了防止这种情况,我会从网站预先查询DISTINCT用户,然后加入用户。我会在(Website_ID,user_ID)的购买表上有一个索引,然后执行以下操作。

select 
      NOW(),
      u.id 
   from 
      ( select distinct p.user_id
           from purchases p
           where p.website_id = 1234 ) PQ
         JOIN users u 
            ON PQ.user_id = u.id
   order by 
      u.total_paid desc 
   limit 
      10;