【问题标题】:Fastest way for this query (What is the best strategy) given a date range给定日期范围,此查询的最快方法(最佳策略是什么)
【发布时间】:2009-08-05 11:52:39
【问题描述】:

我有一个表 A,除了其他一些列之外,还有一个 startDate 和一个 end dateDate 作为 2 个日期时间列。我有另一个表 B,它有一个日期时间列,称为日期列。这是在 SQL Server 2005 中。

这里的问题:如何最好地设置索引等以获得以下内容:

select ....
 from A , B
where A.startDate >= B.dates
  and A.endDate < B.dates

两个表都有几千条记录。

【问题讨论】:

    标签: sql-server sql-server-2005 tsql


    【解决方案1】:

    更新:

    请参阅我的博客中的这篇文章,了解使用计算列的查询的高效索引策略:

    主要思想是我们只为您的范围计算四舍五入的lengthstartDate,然后使用相等条件搜索它们(这对B-Tree 索引很有用)


    MySQLSQL Server 2008 中,您可以使用 SPATIAL 索引 (R-Tree)。

    它们特别适用于“选择记录范围内给定点的所有记录”这样的条件,这正是您的情况。

    您将start_dateend_date 存储为LineString 的开头和结尾(将它们转换为另一个数值的UNIX 时间戳),使用SPATIAL 索引对它们进行索引并搜索所有例如LineStrings,其最小边界框(MBR)包含有问题的日期值,使用MBRContains

    在我的博客中查看此条目,了解如何在MySQL 中执行此操作:

    以及SQL Server 的简要性能概述:

    可以应用相同的解决方案来针对存储在数据库中的网络范围搜索给定的IP

    此任务以及您的查询是此类条件的另一个常用示例。

    如果范围可以重叠,普通的B-Tree 索引就不好。

    如果他们不能(你知道的),你可以使用@AlexKuznetsov提出的绝妙解决方案

    另请注意,此查询性能完全取决于您的数据分布。

    如果您在B 中有很多记录,而在A 中有少量记录,您可以在B.dates 上建立一个索引,然后让A 上的TS/CIS 去。

    此查询将始终从A 读取所有行,并将在嵌套循环中使用B.dates 上的Index Seek

    如果您的数据以其他方式分布,i。 e. A 中有很多行,但 B 中的行很少,而且范围通常很短,那么您可以稍微重新设计一下表格:

    A
    
    start_date interval_length
    

    ,在A (interval_length, start_date)上创建复合索引

    并使用此查询:

    SELECT  *
    FROM    (
            SELECT  DISTINCT interval_length
            FROM    a
            ) ai
    CROSS JOIN
            b
    JOIN    a
    ON      a.interval_length = ai.interval_length
            AND a.start_date BETWEEN b.date - ai.interval_length AND b.date
    

    【讨论】:

    • 这是我看到的让软件依赖于 SQL Server 2008(而不是 2005)的第一个充分理由
    • 我不知道空间索引如何与其他类型的索引结合,例如“where department = 123 and SickLeave OVERLAPS WorldCub”
    • @Ian:在SQL Server中,索引将分开使用,然后使用适当的合并方法合并(很可能是HASH MATCH
    • 对不起,我的意思是,我可以在 (DepartmentId, SicknesStartDate) 上创建一个索引,然后如果我搜索某个部门中在给定时间生病的所有员工,则可以使用该索引.但是,我可以创建包含 R-Trees 的“复合”(因为需要更好的词)索引吗?
    • @Ian:你不能。 B-Tree 索引依赖于排序,而在(col1, col2, com3) 上排序意味着对(col1, col2)col1 进行排序,因此使用B-Tree,您可以以1 的价格获得3 索引。 R-Tree 不是这种情况。
    【解决方案2】:

    【讨论】:

      【解决方案3】:

      我曾在两家公司(都做时间和考勤管理系统)工作过,这两家公司有很多次使用 startDate 和 endDate 列。根据我的经验,总是日期范围 一起使用的索引并不好。

      尝试使用 (startDate, -endDate) 和 (-endDate, startDate) 之类的索引,看看它们是否有帮助,很大程度上取决于表中的数据是什么样的。例如,如果您倾向于在要查找的日期之前有很多带有 endDate 的旧行,则强制 Sql 使用基于 (endDate, startDate) 的索引可能会有所帮助。

      还可以尝试使用覆盖“where”语句中所有列的索引,这样 sql 在计算出要返回的行之前不需要读取主表。

      可能必须使用索引提示,因为查询处理器不太可能对数据有足够的了解来做出好的索引选择——这是极少数之一strong> 不得不考虑索引提示的情况。

      扩展数据,因此您有一个包含(日期,行)的表,其中日期范围内的每个日期都有一行可能需要。然而,保持“索引”表的更新是一件痛苦的事情。

      如果您知道您的某些日期范围不重叠,请查看Using CROSS APPLY to optimize joins on BETWEEN conditions(例如,可能不允许员工的疾病记录重叠)

      归根结底,如果您只有几千条记录,那么全表扫描也不错。

      Quassnoi subjects using SPATIAL indexes,我没有以这种方式“滥用”空间索引的经验,但我认为值得尝试。但是,如果您必须支持多个数据库供应商,请务必小心,因为空间索引是相当新的。此外,您可能仍需要报告工具等的日期列。

      (迟早需要能够找到与日期范围重叠的所有行,然后获得返回良好结果的索引变得更加困难。)

      【讨论】:

        【解决方案4】:

        每个版本的 sql server 2000、2005、2008 都有一个名为 DataBase tune advisor 的程序,当您运行某些查询时,它会告诉您需要添加哪些索引才能更快地查询 此致, 约旦

        【讨论】:

          【解决方案5】:

          你需要3个索引A.startDate、B.dates和A.endDate,可能索引(A.endDate+A.startDate)也不错。 我没有关于这些表的其他列和用途的详细信息,但请查看使用聚集索引的可能性。

          无论如何使用“执行计划”选项在所有这些变体之间做出决定,因为我的建议太笼统了

          【讨论】:

            【解决方案6】:

            以下脚本将列出可能缺少的索引(您可以通过 t.name 过滤语句)。

            SELECT     t.name AS 'affected_table',
                       'Create NonClustered Index IX_' + t.name + '_missing_' + CAST(ddmid.index_handle AS VARCHAR(10)) + ' On ' + ddmid.STATEMENT + ' (' + ISNULL(ddmid.equality_columns, '') +
                       CASE
                                  WHEN ddmid.equality_columns IS NOT NULL
                                         AND ddmid.inequality_columns IS NOT NULL
                                  THEN ','
                                  ELSE ''
                       END + ISNULL(ddmid.inequality_columns, '') + ')' + ISNULL(' Include (' + ddmid.included_columns + ');', ';') AS sql_statement,
                       ddmigs.user_seeks,
                       ddmigs.user_scans,
                       CAST((ddmigs.user_seeks + ddmigs.user_scans) * ddmigs.avg_user_impact AS INT) AS 'est_impact',
                       ddmigs.last_user_seek
            FROM       sys.dm_db_missing_index_groups      AS ddmig
            INNER JOIN sys.dm_db_missing_index_group_stats AS ddmigs
            ON         ddmigs.group_handle = ddmig.index_group_handle
            INNER JOIN sys.dm_db_missing_index_details AS ddmid
            ON         ddmig.index_handle = ddmid.index_handle
            INNER JOIN sys.tables AS t
            ON         ddmid.OBJECT_ID = t.OBJECT_ID
            WHERE      ddmid.database_id = DB_ID()
                   AND CAST((ddmigs.user_seeks + ddmigs.user_scans) * ddmigs.avg_user_impact AS INT) > 100
            ORDER BY   CAST((ddmigs.user_seeks + ddmigs.user_scans) * ddmigs.avg_user_impact AS INT) DESC;
            

            【讨论】:

              【解决方案7】:

              需要更多信息。表中有多少其他列?这些现有表是否已经存在大量查询,或者所有新表?您看到什么样的性能问题导致您提出问题?

              我假设所有三列都不为 NULL(不仅仅是为了查询语法,而是为了索引的有用性)。

              我将从 A.startDate + A.endDate 上的复合索引开始,然后在 B.dates 上创建另一个索引(但这可能不需要)。除非这些日期是表格的主要目的,否则我会避免在这些列上创建聚集索引。如果这些表是现有表并且针对它们运行其他查询,则情况更是如此。以前的查询可能会在预期现有的聚集索引的情况下编写。

              【讨论】:

                【解决方案8】:

                我会选择这个

                CREATE CLUSTERED INDEX IX_DateRange ON dbo.A
                    (
                    StartDate,
                    EndDate DESC
                    ) 
                GO
                

                【讨论】:

                • 这仅适用于刺探查询。顺便说一句,好久不见。 Artima 论坛消失了
                【解决方案9】:

                我只想在 B.dates 上添加一个聚集索引。如果您在 startDate 和 endDate 添加索引,它不会购买任何东西,因为无论如何您都会在 A 上获得索引扫描。 B 上的聚集索引至少可以让您在 B 中进行索引查找。表扫描和索引扫描是一回事,因此添加索引以将表扫描一词排除在执行计划之外是没有意义的:)

                我会用几种方法来模拟它,或者看看你是否可以重做查询以不需要对 A 进行表扫描,我猜这是不可能的。

                【讨论】:

                • “表扫描和索引扫描是一回事”?我不这么认为,除非您的意思是当索引具有表中的所有列时。我非常怀疑他的表格中只有提到的列。
                • 是的,它们是一样的。索引扫描从表中检索每一行,而查找则不会。表上没有其他聚集索引的表扫描(或索引扫描)会给您带来最差的性能。
                • 索引扫描从索引而不是表中检索每一“行”。索引扫描比表扫描更好,因为索引通常比表中的列少,导致每次读取的“行”更多。但我们同意扫描是不好的,应该避免。
                • OP 说有问题的表中只有几千行,因此由于您在此处讨论的含义,任何人都会注意到查询速度的任何差异,这是值得怀疑的。我模拟了一些示例,当我在引用的 tableA 中的各个列上尝试非聚集索引和聚集索引时,OP 讨论的数据大小没有任何差异。
                • 由于我们不知道该表还有哪些其他列以及执行了哪些其他查询,因此我们无法判断放置聚集索引是否会有所帮助
                【解决方案10】:

                如果您需要优化,请尝试在查询分析器中运行此查询。

                【讨论】:

                  猜你喜欢
                  • 1970-01-01
                  • 1970-01-01
                  • 1970-01-01
                  • 1970-01-01
                  • 1970-01-01
                  • 2015-12-07
                  • 2010-10-10
                  • 1970-01-01
                  • 2019-03-24
                  相关资源
                  最近更新 更多