如何使用具有多个 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 在大表上的查询来优化查询?的主要内容,如果未能解决你的问题,请参考以下文章