是否可以进一步优化这些SQL查询?

时间:2011-01-31 18:40:32

标签: sql ruby-on-rails ruby optimization

我有一个Rails应用程序(在Heroku帐户上运行),该应用程序正在抓取主页上有关符合特定条件的记录数量的一组统计信息。每个计数在页面上显示为一个数字。我的表(列表)包含大约22,500条记录。在制作时,页面加载大约需要350毫秒(仍然低于阈值,但主页不是很好)。

请关注这里的查询数量,我想描绘我正在尝试做的冗余。这个感觉就像它可以更有效地完成。有什么想法吗?

SELECT COUNT(1) FROM listings WHERE (city in ('Syracuse'))
SELECT COUNT(1) FROM listings WHERE (city in ('Syracuse')) AND (created_at >= '2011-01-30 18:28:44.656702')
SELECT COUNT(1) FROM listings WHERE (city in ('Cicero', 'Clay', 'Lysander', 'VanBuren', 'Salina'))
SELECT COUNT(1) FROM listings WHERE (city in ('Cicero', 'Clay', 'Lysander', 'VanBuren', 'Salina')) AND (created_at >= '2011-01-30 18:28:44.811090')
SELECT COUNT(1) FROM listings WHERE (city in ('DeWitt', 'Manlius', 'Pompey'))
SELECT COUNT(1) FROM listings WHERE (city in ('DeWitt', 'Manlius', 'Pompey')) AND (created_at >= '2011-01-30 18:28:44.954442')
SELECT COUNT(1) FROM listings WHERE (city in ('Onondaga', 'Elbridge', 'Geddes', 'Camillus'))
SELECT COUNT(1) FROM listings WHERE (city in ('Onondaga', 'Elbridge', 'Geddes', 'Camillus')) AND (created_at >= '2011-01-30 18:28:45.105438')
SELECT COUNT(1) FROM listings WHERE (city in ('Fabius', 'Lafayette', 'Marcellus', 'Otisco', 'Skaneateles', 'Spafford', 'Tully'))
SELECT COUNT(1) FROM listings WHERE (city in ('Fabius', 'Lafayette', 'Marcellus', 'Otisco', 'Skaneateles', 'Spafford', 'Tully')) AND (created_at >= '2011-01-30 18:28:45.258860')
SELECT COUNT(1) FROM listings WHERE (city in ('West Monroe', 'Hastings', 'Constantia', 'Palermo', 'Mexico', 'Parish', 'Schroeppel'))
SELECT COUNT(1) FROM listings WHERE (city in ('West Monroe', 'Hastings', 'Constantia', 'Palermo', 'Mexico', 'Parish', 'Schroeppel')) AND (created_at >= '2011-01-30 18:28:45.411138') 

我考虑过的一个选项是使用我的Listing模型上的after_add和after_remove挂钩来更新带有这些统计信息的单独表。我唯一担心的是涉及的维护问题。但是,新列表只会在一天内添加几次,因此更新所述表格本身不会导致性能问题。

谢谢!

3 个答案:

答案 0 :(得分:4)

各种方法,并非都是面向数据库的。

您可以将所有选择组合成一个查询,如下所示:

SELECT COUNT(CASE WHEN city = 'Syracuse' THEN 1 END) as syracuse,
       COUNT(CASE WHEN city = 'Syracuse' AND created_at >= '2011-01-30 18:28:44.656702' THEN 1 END) as syracuse_recent,
       /* etc... */
FROM listings

这只是在桌面上扫描一次以收集所有统计数据。

或者/另外,将从数据库中提取的统计信息缓存在应用程序的内存中,或者使用类似memcached的内容。如果统计数据不需要最新的准确度,那么在初始填充之后,这将完全从数据库中卸载查询。

答案 1 :(得分:0)

首先,您应该检查您对表有哪些索引(尝试在各个字段上添加和删除索引以及在两个方向上复合索引)。

还要确保准确分析350ms组成的内容(使用firebug或YSlow之类的东西)。

最后,如果你真的有罕见的更新并且你想维护一个汇总表,钩子不是唯一的方法 - 你也可以编写触发器来为你完成这项工作。

答案 2 :(得分:0)

就个人而言,我会添加两个新表,一个包含城市组,另一个包含组和城市之间的多对多链接表。你需要“city_group_id”,“city_group_name”,“dt_count_threshold”。第二个表是“city_group_id”,“city_id”。然后,您可以针对多对多链接表执行选择,并使用您的日期/时间限制加入城市表。

-- unrestricted count
selec cg.city_group_name, count(*) as cnt
from dbo.city_group cg
join dbo.city_group_city cgc on cg.city_group_id = cgc.city_group_id
group by city_group_name

-- restricted
selec cg.city_group_name, count(*) as cnt
from dbo.city_group cg
join dbo.city_group_city cgc on cg.city_group_id = cgc.city_group_id
join dbo.city c on c.city_id = cgc.city_id
group by city_group_name
where c.created_at >= cg.dt_count_threshold

请注意,这些是未经测试的查询,因此可能需要进行一些小的调整。并确保正确设置所有索引以避免表扫描。