如何使用子查询,WHERE IN和varchar比较字段优化查询?

时间:2014-11-05 08:31:29

标签: mysql sql subquery query-optimization

我正在开发一个抓取项目来抓取项目及其在不同时间表上的视图计数。时间表是用户定义的时间段(日期),用于运行脚本。

表结构如下:

CREATE TABLE IF NOT EXISTS `stats` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `schedule_id` smallint(11) NOT NULL,
  `type` smallint(11) NOT NULL,
  `name` varchar(250) COLLATE utf8_unicode_ci NOT NULL,
  `views` int(11) NOT NULL,
  `updated_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
  PRIMARY KEY (`id`)
) ENGINE=MyISAM  DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci ;

所有数据都存储在表格统计信息中,稍后会进行分析,以查看视图中的类型明智增长。

数据如下:

样本集

enter image description here

刮痧是在一段时间内完成的,每个时间表预计会有大约20K条目。时间表可以每天或每周进行,因此数据在5-6中将增长到2-3百万左右个月。

在这些数据上,我需要执行查询以聚合遇到所选时间表范围的相同名称。

例如:

我需要汇总多个时间表的相同项目(名称)。如果选择了时间表1和2,则仅选择两个时间表下的项目。所以这里将是 ItemA ItemB

应在此处计算类型的视图总和。

因此对于时间表1:(更新)

SELECT COUNT(t.`type`) AS count, SUM(t.views) AS view_count 
FROM `stats` t 
INNER JOIN 
( 
   SELECT name,COUNT(name) as c FROM `stats` WHERE `schedule_id` IN (1,2) GROUP BY name HAVING c=2
) t2 ON t2.`name` = t.`name` 
WHERE `schedule_id`=2 GROUP BY type

enter image description here

这是我的预期结果。

但是我已经阅读了使用子查询,WHERE IN,varchar比较字段对于优化查询没有帮助。如何优化这些以获得更好的性能。

Same Type Aggregator的规则如下:

1.在计划ID下,可能存在具有不同类型值的相同名称.account_id,名称和类型的组合将不会重复。

2.明智的聚合器 - 对每种类型下的值进行求和。

我正在使用Python -MySQL中的项目进行抓取,而PHP则用于列出结果。我想知道如何正确组织此表以及查询以获得更好的性能。 请指教。

2 个答案:

答案 0 :(得分:1)

VARCHAR COLUMN

如评论中所述,将varchars存储在字典表中是一种很好的做法。为什么?它们需要比例如int4更多的空间,因此拥有越来越大的表只会占用更多空间,而每个名称可以在另一个表中存储一次。

QUERY PERFORMANCE

WHERE IN实际上意味着规划人员会将schedule_idANY'{1,2}'转换为integer[]类型进行比较,您可以在下方注意到这一类型。

<强>子查询

如果需要聚合数据,有时无法避免子查询。考虑到这一点,请记住并非所有查询都包含1 SELECT语句。实际上,他们很少这样做(除非你的应用程序只有它的一小部分与数据库连接,例如简单的游戏,你只需要存储包含用户和点的信息)

<强> QUERY

您对给定样本数据的查询计划:

select count(type), sum(views) from tmp_test8 a join (select name,count(1) from tmp_test8 where schedule_id in (1,2) group by 1 having count(1) = 2) b
on a.name = b.name where schedule_id = 1;

                                  QUERY PLAN                                  
------------------------------------------------------------------------------
 Aggregate  (cost=23.59..23.60 rows=1 width=8)
   ->  Nested Loop  (cost=11.77..23.59 rows=1 width=8)
         Join Filter: ((a.name)::text = (tmp_test8.name)::text)
         ->  Seq Scan on tmp_test8 a  (cost=0.00..11.75 rows=1 width=524)
               Filter: (schedule_id = 1)
         ->  HashAggregate  (cost=11.77..11.79 rows=2 width=516)
               Filter: (count(1) = 2)
               ->  Seq Scan on tmp_test8  (cost=0.00..11.75 rows=2 width=516)
                     Filter: (schedule_id = ANY ('{1,2}'::integer[]))

但是,您的查询可以在没有连接的情况下重写,因此它只扫描一次表。这是我的建议:

select count, sum(view_count) 
from( 
    select name, count(1) count, sum(case when schedule_id = 1 then views end) view_count 
    from tmp_test8 
    where schedule_id in (1,2) 
    group by 1 
    having count(1) = 2 
    ) foo 
group by 1
                               QUERY PLAN                               
------------------------------------------------------------------------
 HashAggregate  (cost=11.83..11.85 rows=2 width=16)
   ->  HashAggregate  (cost=11.78..11.80 rows=2 width=524)
         Filter: (count(1) = 2)
         ->  Seq Scan on tmp_test8  (cost=0.00..11.75 rows=2 width=524)
               Filter: (schedule_id = ANY ('{1,2}'::integer[]))

两个查询都会产生相同的结果。

答案 1 :(得分:1)

继续我的评论。

如果名称可能有重复的计划ID,那么您当前的查询将无效。最简单的解决方法是将其从 COUNT(名称)更改为 COUNT(DISTINCT schedule_id)

如果对于名称,则schedule_id是唯一的,那么您可以通过为每个计划ID加入一次统计数据来避免子查询: -

SELECT COUNT(t.`type`) AS count, SUM(t.views) AS view_count 
FROM `stats` t 
INNER JOIN stats t1 ON t.name = t1.name AND t1.schedule_id = 1
INNER JOIN stats t2 ON t.name = t2.name AND t2.schedule_id = 2
WHERE t.schedule_id = 1 

这是对您的数据做出一些假设。

虽然有时需要子查询,但MySQL不会使用子查询结果的索引将其与主表连接。