优化SQL查询 - ORDER BY子句 - 在索引中保存前几天的数据?

时间:2017-06-20 15:20:07

标签: sql sql-server select indexing insert

我们有一个大约有3000万行的数据库(使用SQL Server)。使用SELECT子句运行ORDER BY个查询需要很长时间才能完成。

这是查询:

SELECT Top 100 [Column1],[Column2],[Column3],[Column4],
               [Column5],[Column6],Column7],ISNULL([Column8],0),
               ISNULL([Column9],''),[Column10] 
FROM [SQLDB].[dbo].[Data] 
ORDER BY Column5 DESC;

表上已经有一些索引,我避免添加很多索引,因为它会导致偶尔执行的INSERT查询冲突。

我正在考虑通过删除旧数据来减少行数,但它是有价值的内容,因此我不希望立即将其删除。

是否可以将索引中的数据保存一定日期?例如,在Index中存储前10天以便更快地检索(不包括当前日期,其中包含INSERT语句不断更新表格。)

如何优化该查询以返回更快的结果?

2 个答案:

答案 0 :(得分:4)

首先,对于您的查询,Column5上的索引应加快速度。我可以欣赏不要太多的索引。但这是一种方法。

其次,如果你有一个过滤条款,那就用吧!即使WHERE没有索引,也会减少ORDER BY所需的空间量。这是一场胜利。

第三,您可能希望研究分区(请参阅documentation)。这将一个表分成多个不同的存储区域。这可以使查询更有效率,并且更容易丢弃大量的旧版本。行。

答案 1 :(得分:1)

如果您拥有SQL Server 2016 SP1(标准版或企业版)或SQL Server 2005及更高版本的企业版,那么表格分区将是您最好的选择。

但是,如果您只有早期版本的SQL Server的标准版,那么表分区将不是一个选项。

标准版的SQL Server支持过滤索引的概念。您可以在表上创建仅包含固定时间间隔的数据的筛选索引。请注意,不支持最后10天等滚动期。但是,您可以根据以下示例为当月和未来几个月创建过滤索引:

-- Filtered index for June
CREATE NONCLUSTERED INDEX [Data_Date_2017_6] ON [dbo].[Data]
(
    [Column5] ASC
)
INCLUDE (   [Column1],
    [Column2],
    [Column3],
    [Column4],
    [Column6],
    [Column8],
    [Column9],
    [Column10]) 
WHERE ([Column5]>='06/01/2017' AND [Column5]<'07/01/2017')
WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, SORT_IN_TEMPDB = OFF, DROP_EXISTING = OFF, ONLINE = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]

GO

-- Filtered index for July
CREATE NONCLUSTERED INDEX [Data_Date_2017_7] ON [dbo].[Data]
(
    [Column5] ASC
)
INCLUDE (   [Column1],
    [Column2],
    [Column3],
    [Column4],
    [Column6],
    [Column8],
    [Column9],
    [Column10]) 
WHERE ([Column5]>='07/01/2017' AND [Column5]<'08/01/2017')
WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, SORT_IN_TEMPDB = OFF, DROP_EXISTING = OFF, ONLINE = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]

GO

-- Filtered index for August
CREATE NONCLUSTERED INDEX [Data_Date_2017_8] ON [dbo].[Data]
(
    [Column5] ASC
)
INCLUDE (   [Column1],
    [Column2],
    [Column3],
    [Column4],
    [Column6],
    [Column8],
    [Column9],
    [Column10]) 
WHERE ([Column5]>='08/01/2017' AND [Column5]<'09/01/2017')
WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, SORT_IN_TEMPDB = OFF, DROP_EXISTING = OFF, ONLINE = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]

在上面的例子中,我假设[Column5]是一个日期时间列。 将来使用过滤器创建的索引将为空,并且SQL Server将填充它们,因为符合过滤条件的新记录将插入到表中。随着每个月的进展,您可以简单地删除不再适用的月份的过滤索引。您也可以创建自动脚本而不是为您维护这些索引,但是,根据需要手动添加新月并通过脚本删除旧月非常简单。 如果现在在SQL查询中包含where子句以限定定义时间段内的结果,那么SQL Server将只按以下示例命中它所需的索引:

DECLARE @endDateTime DATETIME = '09/01/2017'
DECLARE @startDateTime DATETIME = DATEADD(DAY, -10, @endDateTime);

SELECT TOP 100 
       [Column1]
      ,[Column2]
      ,[Column3]
      ,[Column4]
      ,[Column5]
      ,[Column6]
      ,[Column8]
      ,[Column9]
      ,[Column10]
FROM  [dbo].[Data]
WHERE ([Column5]>= @startDateTime AND [Column5] < @endDateTime) 
ORDER BY [Column5] DESC OPTION (RECOMPILE);

注意OPTION(RECOMPILE);它添加到查询中以强制sql server重新评估查询计划,这将确保SQL Server使用正确的索引。