如何正确索引具有多个联接的查询中使用的表

时间:2011-08-30 17:02:57

标签: sql database indexing

我正在尝试确定下面索引我的查询的最佳方式。

到目前为止,我已经在连接上使用的字段上创建了复合/分组索引,然后是我使用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

注意:我的其他表也有索引。

非常感谢任何帮助

3 个答案:

答案 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

它很好地解释了什么时候/什么索引。

首先,我会为这些创建索引:

  1. articles_to_geo.article_id
  2. cities_whitelist.city_id
  3. cities.city_id
  4. articles_to_badges.article_id
  5. articles_to_badges.badge_id
  6. badges.id
  7. sites.id
  8. 如果没有上述内容,您的联接+ 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_idcities.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子句中出现的字段顺序无关。 如果保留此顺序,则索引可以用于指定

的所有查询
  • 和site_id
  • site_id和expirydate

但是这不能在那些只指定

的查询中使用
  • expirydate
  • expirydate and status