我正在尝试确定下面索引我的查询的最佳方式。
到目前为止,我已经在连接上使用的字段上创建了复合/分组索引,然后是我使用where子句过滤器的顺序。
或者,我应该在连接上使用的字段上创建单独的索引,还是在where / group by / order by clause使用的字段上创建单独的分组索引
SELECT [fields..]
FROM articles
INNER JOIN articles_to_geo
ON articles_to_geo.article_id = articles.article_id
INNER JOIN cities_whitelist
ON cities_whitelist.city_id = articles_to_geo.whitelist_city_id
INNER JOIN cities
ON cities.city_id = cities_whitelist.city_id
INNER JOIN articles_to_badges
ON articles_to_badges.article_id = articles.article_id
INNER JOIN badges
ON badges.id = articles_to_badges.badge_id
INNER JOIN sites
ON sites.id = articles.site_id
WHERE articles.expirydate > '2010-07-12'
AND articles.dateadded > '2010-08-11'
AND articles.status >= 6
AND cities.city_id = 5794
AND cities.timezone = -7
AND cities_whitelist.published = 1
AND articles_to_badges.badge_id IN (1,3,8,7)
ORDER BY sites.sort_order";
例如,我的文章表有一个分组索引:
索引1
article_id
site_id
expirydate
status
dateadded
或者我应该有2个索引吗?
索引1 //用于连接子句
article_id
索引2 //用于where / order by / group by子句
site_id
expirydate
status
dateadded
注意:我的其他表也有索引。
非常感谢任何帮助
答案 0 :(得分:1)
注意:SQL Server是我使用的。如果你正在使用别的东西 - 这可能不适用。 另请注意:我将讨论索引以帮助从表中访问数据。覆盖索引是一个单独的主题,我在这里没有提到。
访问表格时,有3种方法可以做到。
我首先制作了一个包含过滤条件和关系标准的所有表的列表。
articles
articles.expirydate > 'somedate'
articles.dateadded > 'somedate'
articles.status >= someint
articles.article_id <-> articles_to_geo.article_id
articles.article_id <-> articles_to_badges.article_id
articles.site_id <-> sites.id
articles_to_geo
articles_to_geo.article_id <-> articles.article_id
articles_to_geo.whitelist_city_id <-> cities_whitelist.city_id
cities_whitelist
cities_whitelist.published = someint
cities_whitelist.city_id <-> articles_to_geo.whitelist_city_id
cities_whiltelist.city_id <-> cities.city_id
cities
cities.city_id <-> cities_whiltelist.city_id
articles_to_badges
articles_to_badges.badge_id in (some ids)
articles_to_badges.article_id <-> articles.article_id
article_to_badges.badge_id <-> badges.id
badges
badges.id <-> article_to_badges.badge_id
sites
sites.id <-> articles.site_id
最简单的方法是在每个支持每个关系和过滤标准的表上创建一个索引...然后让优化器选择它想要使用的索引。这种方法非常适合IO性能,而且操作简单......但是在未使用的索引中会占用大量空间。
下一个最好的方法是在打开这些选项的情况下运行查询:
SET STATISTICS IO ON
SET STATISTICS TIME ON
如果一组特定的表使用更多IO,则可以将索引工作集中在它们上。要做到这一点,依赖于表访问的顺序的优化器计划已经非常好了。
如果由于缺少索引而优化器根本无法制定好的计划,我所做的就是找出我想要访问的表的顺序,然后添加支持这些访问的索引。
注意:访问的第一个表没有使用关系条件的选项,因为尚未读取任何记录。必须通过Filtering Criteria或Read the Whole Table访问第一个表。
一种可能的顺序是查询中的顺序。这种方法可能非常糟糕,因为我们的文章过滤标准基于3个不同的范围。可能有数以千计的文章符合该标准,并且很难制定支持这些范围的索引。
Articles (Filter)
Articles_to_Geo (Relational by Article_Id)
Cities_WhiteList (Relational by City_Id) (Filter)
Cities (Relational by City_Id) (Filter)
Articles_to_Badges (Relational by Article_Id) (Filter)
Badges (Relational by Badge_Id)
Sites (Relational by Article_Id)
另一个可能的顺序是城市首先。城市标准很容易索引,可能只有一行!查找城市的文章,然后按日期过滤,应该比查找日期的文章,然后过滤到城市更少的行。
Cities (Filter)
Cities_WhiteList (Relational by City_Id) (Filter)
Articles_to_Geo (Relational by City_Id)
Articles (Relational by Article_Id) (Filter)
Articles_to_Badges (Relational by Article_Id) (Filter)
Badges (Relational by Badge_Id)
Sites (Relational by Article_Id)
第三种方法可以是徽章。如果文章很少累积徽章并且徽章数量不多,那将是最好的。
Badges (Read the Whole Table)
Articles_to_Badges (Relational by Badge_Id) (Filter)
Articles (Relational by Article_Id) (Filter)
Articles_to_Geo (Relational by Article_Id)
Cities_WhiteList (Relational by City_Id) (Filter)
Cities (Relational by City_Id) (Filter)
Sites (Relational by Article_Id)
答案 1 :(得分:0)
我建议您阅读:http://hackmysql.com/case4
它很好地解释了什么时候/什么索引。
首先,我会为这些创建索引:
如果没有上述内容,您的联接+ IN()将永远占用
答案 2 :(得分:0)
修改:我从文章索引
中删除了article_id
字段
在过去,RDBMS系统无法在一个表上组合B-Tree索引。请参阅此文http://use-the-index-luke.com/sql/where-clause/searching-for-ranges/index-merge-performance。这意味着例如如果您对此查询中使用的所有文章列都有单独的索引,则只会使用其中一个索引。
仅基于此查询,您应该具有以下索引:
<强>制品强>
site_id
expirydate
status
dateadded
articles_to_geo
article_id
<强> cities_whitelist 强>
city_id
<强>城市强>
网站已加入sites.id = articles.site_id
这里我认为ID是网站上的主键,因此无需cities.city_id
和cities.timezone
上的其他索引,因为它们将成为过滤谓词的一部分
<强> articles_to_badges 强>
article_id
badge_id (or this could be a second index of type Bitmap, refer to the article above)
<强>徽章强> 如果你在id字段上有唯一索引
,也加入了主键,不需要额外的索引关于文章索引的说明: 索引中字段的顺序与where子句中出现的字段顺序无关。 如果保留此顺序,则索引可以用于指定
的所有查询但是这不能在那些只指定
的查询中使用