mysql - 为什么使用 left join+where select 会慢很多

标签 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)

不是 super 快,但相当敏捷。如果我除了将 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 秒内执行,并且 users 表中没有任何字段大于 varchar(255)

但是,如果我从相对快速的查询中获取 id,我只需:

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

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

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

更多更多信息:这两个查询的解释如下:

*************************** 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:

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

最佳答案

首先,我想问/部分回答。你真正想要什么。您对购买表有一个 LEFT-JOIN,但随后有一个针对特定“购买”网站 ID 的 WHERE 子句。这本质上是将查询引入 INNER JOIN,并仅返回那些确实从相关网站购买过的用户。也就是说,我会将查询重写为

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 上购买。这也可能会给出错误的答案,因为如果一个用户从 1234 网站多次购买,会发生什么情况?同一个网站,而且他们是顶级买家之一……他们的 ID 可能会出现多次。为了防止这种情况,我会预先查询站点中的不同用户,然后加入用户。我将在 (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;

关于mysql - 为什么使用 left join+where select 会慢很多,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/18797081/

相关文章:

sql - MS Access Form - 文本框中的字符串,用于 LIKE 查询以过滤报告结果

android - 为什么不直接将android连接到数据库?

mysql - 如何创建 SQL 存储过程以插入多个值

php - 如何从不同文件和数组变量中的 mysql_fetch_assoc() 访问连续行?

mysql - 从嵌套规则生成器生成 MySQL 查询

sql - 将多个查询转换为单行

mysql - 返回列名及其在 MySQL 中的不同值的计数

php - 'this' 中的未知列 'where clause'

mysql - 删除该键的记录后,自动重新分配 MySql 表中主键的值

mysql - MySQL 中的 InnoDB 和 MyISAM 是什么?