如何使用具有多个 GROUP BY、子查询和 WHERE IN 在大表上的查询来优化查询?

Posted

技术标签:

【中文标题】如何使用具有多个 GROUP BY、子查询和 WHERE IN 在大表上的查询来优化查询?【英文标题】:How to optmize query with a query with multiple GROUP BY's,sub queries and WHERE IN over a large table? 【发布时间】:2015-01-29 12:29:48 【问题描述】:

我正在开展一个抓取项目,以在不同的时间表上抓取项目及其分数。时间表是脚本打算运行的用户定义的时间段(日期)。

表结构如下:

--
-- Table structure for table `test_join`
--

CREATE TABLE IF NOT EXISTS `test_join` (
  `schedule_id` int(11) NOT NULL,
  `player_name` varchar(50) NOT NULL,
  `type` enum('celebrity','sportsperson') NOT NULL,
  `score` int(11) NOT NULL,
  PRIMARY KEY (`schedule_id`,`player_name`,`type`)
) ENGINE=MyISAM DEFAULT CHARSET=latin1;

--
-- Dumping data for table `test_join`
--

INSERT INTO `test_join` (`schedule_id`, `player_name`, `type`, `score`) VALUES
(1, 'sachin', 'sportsperson', 100),
(1, 'ganguly', 'sportsperson', 80),
(1, 'dravid', 'sportsperson', 60),
(1, 'sachin', 'celebrity', 100),
(2, 'sachin', 'sportsperson', 120),
(2, 'ganguly', 'sportsperson', 100),
(2, 'sachin', 'celebrity', 120);

抓取是分时段进行的,对于每个计划,预计会有大约 10k+ 个条目。计划可以每天进行,因此数据将在 5-6 个月内增长到大约 200 万。

根据这些数据,我需要执行查询以汇总在选定的赛程范围内遇到每个赛程的玩家。

例如:

我需要汇总遇到多个赛程的相同球员。如果选择了时间表 1 和 2,则只会选择同时属于这两个时间表的项目。

我正在使用以下查询来根据类型聚合结果,

对于时间表 1:

SELECT fullt.type,COUNT(*) as count,SUM(fullt.score) FROM
(SELECT tj.*
FROM `test_join` tj
RIGHT JOIN 
(SELECT `player_name`,`type`,COUNT(`schedule_id`) as c FROM `test_join` WHERE `schedule_id` IN (1,2) GROUP BY `player_name`,`type` HAVING c=2) stj 
on tj.player_name = stj.player_name 
WHERE tj.`schedule_id`=1
GROUP BY tj.`type`,tj.`player_name`)AS fullt
GROUP BY fullt.type

c = 2 的原因;

WHERE `schedule_id` IN (1,2) GROUP BY `player_name`,`type` HAVING c=2 

这里我们选择了两个计划,1 和 2。因此计数 2 用于进行查询以获取属于这两个计划并发生两次的记录。

它会产生如下结果,

时间表 1:预期结果

附表 2:预期结果

这是我的预期结果,查询返回的结果如上。 (在实际情况下,我必须处理相当大的 mysql 表)

根据我对标准化 MySQL 查询的理解,使用子查询、WHERE IN、varchar 比较字段、多个 GROUP BY 会影响查询性能。 我需要实时汇总结果,查询速度和标准也是一个问题。在这种情况下如何优化它以获得更好的性能。

编辑:

我现在减少了子查询:

SELECT fullt.type,COUNT(*) as count,SUM(fullt.score) FROM (
SELECT t.*
FROM `test_join` t 
INNER JOIN test_join t1 ON t.`player_name` = t1.player_name AND t1.schedule_id = 1
INNER JOIN test_join t2 ON t.player_name = t2.player_name AND t2.schedule_id = 2
WHERE t.schedule_id = 2 
GROUP BY t.`player_name`,t.`type`) AS fullt
GROUP BY fullt.type

这是一种更好的方法吗?我已将 WHERE IN 替换为 JOINS。

任何建议都将受到高度赞赏。如果需要,我很乐意提供任何支持信息。

【问题讨论】:

您的示例结构、数据和结果都可以,但是,您实际上想要做什么。查询的真正目的是什么(上下文,而不仅仅是获取计数和总和)。如果处理大量数据,最好先完成一个预聚合表并从中查询,而不是翻阅数百万条记录。 @DRapp 查询的真正目的是实时获取汇总详细信息。我将选择一系列时间表,例如时间表 1,2 和 3;我应该得到那些属于所有三个时间表的球员,以及每个时间表的按类型计数和得分总和。我不清楚每个聚合表,在我的情况下,用户将随机选择各种计划,并相应地填充结果。我知道翻阅整套记录有一定的局限性,但我想知道如何以更好的方式做到这一点。 【参考方案1】:

在 MYSQL 中尝试下面的 SQL 查询:

SELECT tj.`type`,COUNT(*) as count,SUM(tj.`score`) FROM
`test_join` tj
where tj.`schedule_id`=1 
and `player_name` in 
(
select tj1.`player_name` from `test_join` tj1 
group by tj1.`player_name` having count(tj1.`player_name`) > 1  
)
group by tj.`type`

实际上我在 Sybase 中尝试了相同的数据,因为我的机器上没有安装 MySQL。它按预期工作!

CREATE TABLE #test_join
 (
  schedule_id int NOT NULL,
  player_name varchar(50) NOT NULL,
  type1 varchar(15) NOT NULL,
  score int NOT NULL,
 ) 

INSERT INTO #test_join (schedule_id, player_name, type1, score) VALUES
(1, 'sachin', 'sportsperson', 100)



INSERT INTO #test_join (schedule_id, player_name, type1, score) VALUES(1, 'ganguly', 'sportsperson', 80)
INSERT INTO #test_join (schedule_id, player_name, type1, score) VALUES(1, 'dravid', 'sportsperson', 60)
INSERT INTO #test_join (schedule_id, player_name, type1, score) VALUES(1, 'sachin', 'celebrity', 100)
INSERT INTO #test_join (schedule_id, player_name, type1, score) VALUES(2, 'sachin', 'sportsperson', 120)
INSERT INTO #test_join (schedule_id, player_name, type1, score) VALUES(2, 'ganguly', 'sportsperson', 100)
INSERT INTO #test_join (schedule_id, player_name, type1, score) VALUES(2, 'sachin', 'celebrity', 120)

select * from #test_join


Print 'Solution #1 : Inner join'

select type1,count(*),sum(score) from 
#test_join
where schedule_id=1 and player_name in (select player_name from #test_join t1 group by player_name having count(player_name) > 1  )
group by type1



select player_name,type1,sum(score) Score into #test_join_temp 
from #test_join 
group by player_name,type1
having count(player_name) > 1

Print 'Solution #2 using Temp Table'
--select * from #test_join_temp 
select type1,count(*),sum(score) from 
#test_join
where schedule_id=1 and player_name in (select player_name from #test_join_temp )
group by type1

我希望这会有所帮助:)

【讨论】:

//select tj1.player_name from test_join tj1 group by tj1.player_name having count(tj1.player_name) > 1 // 你为什么使用 - having count(tj1.player_name) > 1 ,1从哪里来。当我必须通过n个时间表进行选择时应该怎么做?另外,您认为这是一次获得性能的好尝试吗? Count > 1 将决定参加多个赛程的玩家。性能肯定会比您的查询更好。由于您拥有真实数据,因此您可以验证每个时间表的时间并做出决定。谢谢。 //Count > 1 将决定,属于多个时间表的玩家。// 在实际情况下,我有很多时间表,例如:如果用户选择 1,3,5 ,7,9,10 时间表。根据您的查询,结果如何?就我而言:在上述情况下;子查询如下: (SELECT player_name,type,COUNT(schedule_id) as c FROM test_join WHERE schedule_id IN (1,3,5,7,9,10) GROUP BY player_name,type HAVING c=6)

以上是关于如何使用具有多个 GROUP BY、子查询和 WHERE IN 在大表上的查询来优化查询?的主要内容,如果未能解决你的问题,请参考以下文章

JPA Group by 具有多个字段

如何在mongodb的单个查询中添加具有不同条件输出的Multiple Group By?

SUM GROUP BY与多个表上的子查询

使用 GROUP BY/HAVING 重构子查询?

【MySQL】分组查询(GROUP BY)

优化 SQL:如何重写此查询以提高性能? (使用子查询,摆脱 GROUP BY?)