Warning: file_get_contents(/data/phpspider/zhask/data//catemap/8/mysql/69.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Mysql 如何在HAVING子句中优化具有内部SELECTs的查询_Mysql - Fatal编程技术网

Mysql 如何在HAVING子句中优化具有内部SELECTs的查询

Mysql 如何在HAVING子句中优化具有内部SELECTs的查询,mysql,Mysql,我有一个查询,通过他们在比赛中的平均得分来选择前三名篮球运动员。如果篮球运动员在其球队中打了50%以上的比赛,则可被列入前三名: SELECT games_stats.player, AVG(games_stats.points) AS points_avg FROM games_stats WHERE EXISTS ( SELECT * FROM players WHERE games_stats.player = players.id AND status = 'active') AND s

我有一个查询,通过他们在比赛中的平均得分来选择前三名篮球运动员。如果篮球运动员在其球队中打了50%以上的比赛,则可被列入前三名:

SELECT games_stats.player, AVG(games_stats.points) AS points_avg
FROM games_stats
WHERE EXISTS (
SELECT *
FROM players
WHERE games_stats.player = players.id AND status = 'active') AND season = 28293
GROUP BY games_stats.player
HAVING COUNT(games_stats.game) >= ((
SELECT COUNT(*)
FROM games
WHERE home IN (
SELECT team
FROM teams_players
WHERE season='28293' AND player=games_stats.player) AND season='28293' AND (STATUS='finished' OR STATUS='complete')) + (
SELECT COUNT(*)
FROM games
WHERE away IN (
SELECT team
FROM teams_players
WHERE season='28293' AND player=games_stats.player) AND season='28293' AND (STATUS='finished' OR STATUS='complete'))) / 2
ORDER BY points_avg DESC
LIMIT 3
问题是这个查询在服务器资源和运行时都非常昂贵:执行甚至需要0.54秒,我的篮球网站的服务器经常因此过载,有时甚至崩溃。缓存查询结果是不够的,只是有一点帮助,因为篮球比赛几乎每天都在进行,统计数据几乎每天都在更新

我有一个想法,希望它能减少执行时间:我试图减少这个重复的子查询:

SELECT team
FROM teams_players
WHERE season='28293' AND player=games_stats.player
这意味着我想把我的查询变成这样:

SELECT games_stats.player, AVG(games_stats.points) AS points_avg, CONCAT(SELECT team FROM teams_players WHERE season=28293 AND teams_players.player=games_stats.player) AS ids_of_teams
FROM games_stats
WHERE EXISTS (
SELECT *
FROM players
WHERE games_stats.player = players.id AND status = 'active') AND season = 28293
GROUP BY games_stats.player
HAVING COUNT(games_stats.game) >= ((
SELECT COUNT(*)
FROM games
WHERE home IN ids_of_teams AND season='28293' AND (STATUS='finished' OR STATUS='complete')) + (
SELECT COUNT(*)
FROM games
WHERE away IN ids_of_teams AND season='28293' AND (STATUS='finished' OR STATUS='complete'))) / 2
ORDER BY points_avg DESC
LIMIT 3
不幸的是,CONCAT()返回一个串联的团队ID字符串(我需要一个数组)。因此,主要问题是:如何减少/优化这个重复的子查询?如何设置一个“字段”来“存储”重复子查询中得到的ID数组

编辑:现在我发现我的问题是错的——似乎问题出在不同的地方。我有一个问题,我是否可以优化具有内部SELECT查询的HAVING子句

顺便问一下,你有没有其他的想法来写关于最好的球员和他们的数据的更有效的查询?只需注意,我必须“选择这些前三名球员,他们在他们的球队中打了50%以上的比赛”


数据库结构说明: 表“球员”存储了篮球联赛中每个球员的数据。球员可以在下一个赛季或本赛季更换球队,因此数据透视表“球队球员”描述了球员在职业生涯中出现的球队

数据透视表“teams\u players”具有外键“team”、“player”和“seasure”,引用表“teams”、“players”和“seasures”的ID

表“games”存储有关游戏的数据;“主场”和“客场”字段存储游戏中对方球队的ID

表“games_stats”按游戏存储每个玩家的统计数据。它有外键“game”,引用games.id。它还有外键“player”,引用players.id

编辑:解释的输出:


我认为在临时表的帮助下,它应该更简单、更高效。用实际的小提琴很难说:

SET @var_season := 28293;

DROP TEMPORARY TABLE IF EXISTS tmp_team_games_played;
CREATE TEMPORARY TABLE tmp_team_games_played
    (PRIMARY KEY (id))
SELECT t.id, COUNT(0) AS Count
FROM games g
JOIN teams t ON t.id IN (g.home, g.away)
WHERE TRUE
    AND g.season = @var_season AND (g.STATUS IN ('finished', 'complete'))
GROUP BY t.id
;

DROP TEMPORARY TABLE IF EXISTS tmp_player_team_points;
CREATE TEMPORARY TABLE tmp_player_team_points
    (PRIMARY KEY (player, team))
SELECT gs.player, gs.team, SUM(gs.points) AS points_in_team_games
FROM games_stats gs
JOIN players p ON gs.player = p.id AND p.status = 'active' -- reorder based on index
WHERE TRUE
    AND gs.season = @var_season
GROUP BY gs.player, gs.team
;

SELECT tptp.player, AVG(tptp.points_in_team_games) AS points_avg
FROM tmp_player_team_points tptp
JOIN tmp_team_games_played tgp ON tptp.team = tgp.id
GROUP BY tptp.player
-- I took the liberty to make a player play more than a half
-- as opposed to greater than or eqaul to half
HAVING COUNT(0) > (MIN(tgp.Count) * 2)
ORDER BY points_avg DESC
LIMIT 3
;
HAVING子句(太复杂)使我认为这个查询可以用其他方式编写

你能检查一下,并对结果发表评论吗

SELECT 
  games_stats.player, 
  AVG(games_stats.points) AS points_avg
FROM games_stats
INNER JOIN (
  select team
  from games
  inner join teams_players on (teams_players.team=home OR teams_players.team=away)
                           and teams_players.season=games.season
  where games.season=28293
    and (games.status='finished' or games.status='complete')
  ) x on x.team=games_stats.team
WHERE 
  season=28293
  and EXISTS (
    SELECT *
    FROM players
    WHERE games_stats.player = players.id AND status = 'active') 
GROUP BY games_stats.player
ORDER BY points_avg DESC
LIMIT 3;

“我试图减少这个重复的子查询”。。。。。您不必这样做,因为MySQL优化器应该为您这样做!显示此查询的输出,请添加您正在使用的确切MySQL版本。@Nae,值“完成”和“完成”来自游戏表的状态字段。游戏可以只是“完成”,但不能从API导入,“完成”是完全完成的,所有数据都是从体育赛事API导入的。如果一名球员玩了一队游戏的一半,另一队游戏的一半,会怎么样?@有球队的桌子吗?谢谢你的关注和努力。我通过简单地复制/粘贴到MySQL查询窗口检查了您的解决方案-当然它不起作用:D没有错误,但结果为0。明天我将对其进行分析,并稍微研究一下MySQL变量和临时表,因为我缺乏这方面的知识。现在,我向你们表示衷心的感谢,我投票赞成你们的答案,感谢你们的关注,感谢你们的努力和专业知识:)谢谢你们的解决方案。最近我被要求回到这个问题上来,你的解决方案是我提出的最接近的解决方案。我只是创建了一个表,而不是最佳玩家的临时表,cron job每天更新一次最佳玩家的记录。Cron作业使用了我上面展示的这个复杂而缓慢的查询。所以,总而言之,你的解决方案是最好的,篮球网站现在工作得非常完美!不再需要缓存-现在“缓存”是最好的玩家列表:)谢谢!:)谢谢你努力帮助我。我对你的答案做了一点修改——修复了数据库表及其字段的名称。不幸的是,您的查询执行时间要长40倍(最多20秒)——这甚至更糟。我甚至在我的SQL应用程序中终止了查询的执行。这不好,但在不知道数据的情况下编写查询是很困难的。。。。Myabe你能创建一个?