大表中查询的推荐索引,涉及“日期范围”和“订单ID”

时间:2008-11-23 07:38:50

标签: sql sql-server indexing

我有一个查询(由LINQ to SQL创建),以获取在某个日期范围之间进行的“网站访问”列表,该日期范围导致订单(orderid不为空)。

查询没有错。我只需要为它创建正确的索引的建议。我正在玩生产网站上尝试不同的组合,并设法搞砸了外键,以便外键断开连接。我解决了一些恐慌 - 但我想在重建索引之前我会征求意见。

该表接近一百万行,我需要索引来帮助我。此查询仅用于报告,因此不必非常快,只是不要延迟其他用户的查询(它正在进行)。

SELECT TOP 1000
  t0.SiteVisitId, t0.OrderId, t0.Date, 
  t1.Domain, t0.Referer, t0.CampaignId
FROM 
  SiteVisit AS t0
  LEFT OUTER JOIN KnownReferer AS t1 ON t1.KnownRefererId = t0.KnownRefererId
WHERE
  t0.Date <= @p0 
  AND t0.Date >= @p1
  AND t0.OrderId IS NOT NULL
ORDER BY 
  t0.Date DESC

@p0='2008-11-1 23:59:59:000', @p1='2008-10-1 00:00:00:000'

我目前在SiteVisitId上有一个聚集索引,这是我的标识整数列。

我不知道以下哪一项最有效:

  • Date
  • 上创建索引
  • Date上创建索引,在OrderId
  • 上创建单独的索引
  • Date AND OrderId
  • 上创建“多列”索引
  • 其他一些组合?

我也想知道是否应该为hasOrder创建一个单独的位列,而不是检查OrderId IS NOT NULL是否可以提高效率。

仅供参考:KnownReferer只是一个包含100个左右已知HttpReferers列表的表格,因此我可以轻松查看来自谷歌,雅虎等的点击次数。

5 个答案:

答案 0 :(得分:2)

您希望在典型日期范围之间有多少行?你通常一次看一个月吗?

我会从[Date]列的索引开始。如果对于典型查询,结果行数很少,则不需要将[OrderId]列添加到索引中。

另一方面,如果您在一个月中有大量行,那么您可以将[OrderId]列添加到索引中,但由于它被视为布尔值,因此可能无法购买你呢。这取决于NULLNOT NULL的行数。如果您在给定月份内拥有大量行,但只有少数行具有有效[OrderId],那么索引可能会提高性能。

阅读此相关问题中的已接受答案,并确定是否值得对其他列进行索引:

Should I index a bit field in SQL Server?

当然,测试索引和使用而不是索引生成的计划。

更新:其他一些答案指定了更具侵略性的索引,这可以提高此查询的性能,但可能会对表上的其他操作产生负面影响。例如,建议的覆盖索引将允许SQL Server处理此查询而对实际表的影响很小,但是当其他查询写入实际表时可能会导致问题(因为SQL Server需要同时更新表和覆盖索引)那个案子)。

因为这是一个报告查询,所以我会尽可能少地优化它。如果此查询运行时间过长,导致其他更关键的查询运行缓慢或超时,我只会优化此查询以减少它对其他查询的影响。

但是,如果您希望此表继续增长,我会考虑单独的报告模式,并定期从此表​​中提取数据。

答案 1 :(得分:1)

我会在Date和OrderId以及INCLUDE列上创建索引SiteVisitId,Referer,CampaignId(假设您使用的是SQL Server 2005以上)。还要在外键列KnownRefererId上创建索引。

鉴于这是一个报告查询并且可以承受奇怪的未排列行,我建议使用NOLOCK(或READ UNCOMMITED提示):

using (var trans = new TransactionScope(TransactionScopeOption.Required,
                      new TransactionOptions
                      {
                          IsolationLevel = IsolationLevel.ReadUncommitted
                      }))
{
    // Put your linq to sql query here
}

Ref

警告:仅在您有非常好的理由的情况下使用NOLOCK提示。在过去,我看到开发人员通过一揽子使用而感到悲伤!

答案 2 :(得分:0)

如果您需要在SiteVisit中存储在KnownReferer表中没有KnownRefererId且具有Null OrderId的行,也值得考虑。如果您不需要这些,请更改从表中删除它们,并将聚簇索引更改为SiteVisitId和Date,并且查询应该非常快。

但我确定你存储这些额外的行是有原因的。

答案 3 :(得分:0)

如果你真的想要从这个查询中优化bejesus并且你可以接受稍微慢一点的表插入,你可以创建一个索引: -

(Date, OrderId, SiteVisitId, Domain, Referer, CampaignId)

这将使数据库完全从索引返回一个答案,而不进行任何排序或单独的表访问。

答案 4 :(得分:0)

SELECT TOP 1000
  t0.SiteVisitId, t0.OrderId, t0.Date, 
  t1.Domain, t0.Referer, t0.CampaignId
FROM 
  SiteVisit AS t0
LEFT OUTER JOIN KnownReferer AS t1 ON t1.KnownRefererId = t0.KnownRefererId
WHERE
  t0.Date <= @p0 
  AND t0.Date >= @p1
  AND t0.OrderId IS NOT NULL
ORDER BY 
  t0.Date DESC

@p0='2008-11-1 23:59:59:000', @p1='2008-10-1 00:00:00:000'

我将在这里猜测表格统计数据,由此产生的设计可能会减慢其他查询 - 但这通常是权衡。我经常发现,在移动聚簇索引时,最好创建一个替换索引,以避免过多地扰乱其他查询。

假设在1个月的日期范围内有很多行,并且相对较少的行具有OrderId IS NULL - 您最好在Date上使用聚簇索引。这应该给你一个聚集索引扫描,结果nicleley为你的TOP 1000订购。

您可能还希望KnownReferer.KnownRefererId成为聚簇索引或带有knownRefererId + Domain的组合索引,以避免查找该表。我猜你的KnownReferers的数量很小 - 所以我不希望从中获得太多好处。