我们有一个大约有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
语句不断更新表格。)
如何优化该查询以返回更快的结果?
答案 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使用正确的索引。