【问题标题】:Full table scan when doing subquery进行子查询时进行全表扫描
【发布时间】:2015-04-15 21:23:56
【问题描述】:

我有一个关于获取玩家和他的朋友的非常简单的查询,但它似乎进行了全表扫描,我看不出它的原因。我是否需要将其重写为连接以使其使用索引?玩家表有 7411 行。

SQL:

SELECT id, credits, name, image_url FROM player WHERE ID = '999'  
or ID in(select friend_id from player_friends where player_id = '999')

解释:

'1','PRIMARY','player','ALL','PRIMARY',NULL,NULL,NULL,'6994','Using where'
'2','DEPENDENT SUBQUERY','player_friends','ref','index2','index2','198','const,func','1','Using where; Using index'

架构:

CREATE TABLE `player` (
`ID` varchar(32) NOT NULL,
`Name` varchar(45) DEFAULT NULL,
`Credits` decimal(12,3) DEFAULT NULL,
`Access_token` varchar(255) DEFAULT NULL,
`Registered` timestamp NULL DEFAULT CURRENT_TIMESTAMP,
`Image_url` varchar(500) DEFAULT NULL,
`email` varchar(255) DEFAULT NULL,
PRIMARY KEY (`ID`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8$$


CREATE TABLE `player_friends` (
 `id` int(11) NOT NULL AUTO_INCREMENT,
`player_id` varchar(32) DEFAULT NULL,
`friend_id` varchar(32) DEFAULT NULL,
PRIMARY KEY (`id`),
UNIQUE KEY `index2` (`player_id`,`friend_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8$$

【问题讨论】:

  • join 的性能优于子查询
  • 您是否在查询中尝试过 AND 而不是 OR?
  • 子选择返回多少行?如果有很多(EXPLAIN 输出是否显示 198?不确定如何读取),FTS 将比通过索引更快。
  • @Imran 他们为什么要这样做?这完全改变了查询的含义。假设玩家不能和自己成为朋友,那么 ID 不可能既等于他们的 ID,并且 ID 在子查询的结果中。
  • @Thilo 似乎这是一个 MySQL 错误:-/ bugs.mysql.com/bug.php?id=63701。我在 MySQL 5.5 上使用 Google Cloud SQL。

标签: mysql sql


【解决方案1】:

首先,编写查询以使用exists:

SELECT id, credits, name, image_url
FROM player p
WHERE ID = '999' OR
      EXISTS (select 1
              from player_friends pf
              where pf.player_id = '999' and pf.friend_id = p.id
             );

这可能会更有效率。如果它不能满足您的需求,请考虑将其拆分为两个查询:

SELECT id, credits, name, image_url
FROM player p
WHERE ID = '999' 
UNION
SELECT id, credits, name, image_url
FROM player p
WHERE EXISTS (select 1
              from player_friends pf
              where pf.player_id = '999' and pf.friend_id = p.id
             );

注意使用UNION 删除重复项。

【讨论】:

  • 嗯,我得到了第一个查询的全表扫描,第二个查询还说 UNION 运行全表扫描。当我正在测试的用户有 3 个朋友时,这两个 SQL 还返回 > 1000 行:-)
【解决方案2】:

尝试加入:

SELECT p1.id, p1.credits, p1.name, p1.image_url 
FROM player p1
LEFT JOIN player_friends pf ON pf.player_id = p1.id
WHERE p1.id = '999' OR pf.friend_id = '999'

【讨论】:

  • 当我对该语句运行解释时仍然会进行全表扫描,我需要在同一查询中获取所有朋友的 id、信用、姓名和 image_url,而此查询只会获取主要玩家的字段。
  • 修复了条件;对于完整扫描,您可能在 player 和 player_friends 之间缺少 FK\index
  • 解释在额外的列和 9330 行中什么也没说,键为 NULL。这听起来不太好,还是?我尝试在两列上添加来自 player_friends 的外键,但对解释语句没有影响。
猜你喜欢
  • 2017-10-22
  • 2023-04-03
  • 1970-01-01
  • 1970-01-01
  • 2017-06-24
  • 2016-02-03
  • 2021-06-14
  • 2021-08-29
  • 1970-01-01
相关资源
最近更新 更多