1

我有一个查询(由 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 个左右已知 HttpReferer 列表的表格,因此我可以轻松查看来自 google、yahoo 等的点击量。

4

5 回答 5

2

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

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

另一方面,如果您在一个典型的月份中有大量行,那么您可以将该[OrderId]列添加到索引中,但由于它被视为布尔值,它可能不会给您带来太多收益。NULL这取决于vs有多少行NOT NULL。如果给定月份有很多行,但只有少数行有效[OrderId],那么索引可能会提高性能。

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

我应该在 SQL Server 中索引一个位字段吗?

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

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

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

不过,如果您希望该表继续增长,我会考虑使用单独的报告模式并定期从该表中提取数据。

于 2008-11-23T08:34:48.703 回答
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
}

参考

警告:仅在有充分理由的情况下使用 NOLOCK 提示。在过去,我看到开发人员因使用毯子而感到悲痛!

于 2008-11-23T09:47:06.890 回答
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 为 NULL 的行相对较少 - 你最好在 Date 上使用聚集索引。这应该会给你一个聚集索引扫描,结果 nicleley 为你的 TOP 1000 排序。

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

于 2008-11-23T15:19:48.357 回答
0

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

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

于 2008-11-23T09:17:27.183 回答
0

如果您真的想优化此查询中的 bejesus 并且您可以接受稍微慢一点的插入到表中,您可以在以下位置创建索引:-

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

这将使数据库完全从索引中返回答案,而无需任何排序或单独的表访问。

于 2008-11-23T09:38:07.610 回答