【问题标题】:Recommended indexes for query in large table involving a 'date range' and an 'order id'在涉及“日期范围”和“订单 ID”的大表中查询的推荐索引
【发布时间】:2010-09-23 15:50:00
【问题描述】:

我有一个查询(由 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 等的点击量。

【问题讨论】:

    标签: sql sql-server indexing


    【解决方案1】:

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

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

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

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

    Should I index a bit field in SQL Server?

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

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

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

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

    【讨论】:

      【解决方案2】:

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

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

      【讨论】:

        【解决方案3】:

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

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

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

        【讨论】:

        • 除非被证明是 100% 必要的,否则我不会在索引中包含那么多列。如果使用 SQL Server 2005,最好将删除 bookup 查找的列包含到聚集索引中(此外,域在另一个表中!)
        【解决方案4】:

        我会在 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 提示。在过去,我看到开发人员因使用毯子而感到悲痛!

        【讨论】:

        • +1 表示优秀的索引建议,-0.5 表示建议 NOLOCK;它只会鼓励一直使用它。比尝试 UNCOMMITED 路由更好地调整索引方案以减少读取的记录。
        • @SqlACID:这就是为什么我提出“警告:仅在有充分理由的情况下使用 NOLOCK 提示”!
        • 报告可以“承受”未提交的行吗?嗯?这就像说科学可以与少数不真实的事实相处得很好。说真的,SQL Server 人对“TRUTH”的看法完全不同。如果回滚了怎么办?您将获得一份包含该记录的报告,并且任何地方都没有记录。
        【解决方案5】:
        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 的数量很少 - 所以我不希望从中获得太多好处。

        【讨论】:

          猜你喜欢
          • 2016-07-26
          • 2018-03-27
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2011-02-15
          • 2019-04-19
          • 2019-10-17
          相关资源
          最近更新 更多