SQL查询连接表和having子句

时间:2016-09-13 22:28:10

标签: mysql group-by aggregate-functions

我正在用SQL编写一个查询来提取35岁赛季的球员本垒打总数,他们在职业生涯中已经打了500多个本垒打。

SELECT b.playerID, b.yearID, b.HR
FROM batting b
JOIN master m ON m.playerID = b.playerID
WHERE b.yearID - m.birthYear = '35'
HAVING SUM(b.HR) > 500

此查询在执行时超时。我已成功创建一个查询,以返回特定年龄段的玩家本垒打总数。我还成功地创建了一个查询,以返回500家本垒打俱乐部的球员。

当我尝试将它们结合起来时,某些事情会让它超时,我无法确定原因。

这是一个效果很好的查询:

SELECT b.playerID, b.yearID, b.HR
FROM batting b
JOIN MASTER M ON b.playerID = m.playerID
WHERE b.yearID - m.birthYear = 35 AND b.yearID = 2015
ORDER BY b.HR DESC

现在如果我只能归还那些在这个结果中击中500个职业本垒打的球员。 2015年仅有500名本垒打击球手。

1 个答案:

答案 0 :(得分:0)

最可能的解释是优化程序选择的执行计划效率不高。

我们没有看到的是这些表格中可用的索引。

关于查询突出的一点是:

WHERE b.yearID - m.birthYear = '35'

MySQL将使用给定的player_id从master获取每一行,并将其与batting中每一行匹配相同的player_id(由于等式连接谓词)

  ON m.playerID = b.playerID

然后MySQL必须采用那组合并的行,然后计算这个表达式

    b.yearID - m.birthYear

然后从中获取结果并将其与'35'进行比较。

假设playerID列上的master列是唯一的

我们希望看到以可以利用batting上具有(playerID,yearID)前导列的索引的形式编写的查询谓词。

 SELECT b.playerid
      , b.yearid
      , SUM(b.hr) AS hr
   FROM master m
   JOIN batting b
     ON b.playerid = m.playerid
    AND b.yearid   = m.birthyear + 35
  GROUP BY b.playerid, b.yearid
HAVING SUM(b.hr) > 500
 ORDER BY SUM(b.hr) DESC

要为每个玩家返回行,您需要一个GROUP BY子句。要获得总的本垒打,您将需要SELECT列表中的SUM()聚合。

为了获得最佳的查询效果,您需要覆盖索引

... ON batting (playerid, yearid, hr)

如果playeridmaster表上不唯一,则查询不会保证您对SUM(b.hr)的期望值,该值可能是double,triple等预期的是什么。

使用EXPLAIN查看执行计划。

还要注意可能对执行计划产生负面影响的隐式数据类型转换。我们假设两个表中playerid列的数据类型匹配,yearidbirthyear列的数据类型是数字。

修改

我原来的答案主要集中在你的查询“超时”的原因上,而我错过了你想要达到的结果的规范:

返回 职业 人力资源总数超过500的玩家,并返回 总人力资源每个球员的年份。

(我将讨论适当确定玩家年满35岁的“年份”,并使用原始查询中的标准。)

一种方法是使用条件聚合。使用在条件为TRUE时返回HR的表达式,否则返回0或NULL。然后将该表达式包装在SELECT列表中的SUM聚合中。

如果我们想为职业人力资源总数超过500且在指定年份batting至少有一行的玩家返回行...

 SELECT b.playerid
      , MAX(IF(b.yearid = m.birthyear + 35,b.yearid,NULL)) AS yearid
      , SUM(IF(b.yearid = m.birthyear + 35, b.hr, 0)) AS year_hr
   FROM master m
   JOIN batting b
     ON b.playerid = m.playerid
  GROUP BY b.playerid
HAVING SUM(b.hr) > 500
   AND MAX(IF(b.yearid = m.birthyear + 35,b.yearid,NULL)) IS NOT NULL
 ORDER BY ... 

要为每个职业人力资源总数超过500的玩家返回行数,即使指定batting的{​​{1}}中没有行,我们也可以调整查询以省略第二个条件HAVING子句,并在SELECT列表中使用表达式m.birthyear + 35

yearid

请注意,职业人力资源总数恰好为500的玩家将被排除在外。