Ste*_*old 3 index sql-server-2008 sql-server execution-plan
我有一个SELECT GROUP BY
操作的性能瓶颈。
我有一张这样的表:
CREATE TABLE [InverterData](
[InverterID] [bigint] NOT NULL,
[TimeStamp] [datetime] NOT NULL,
[ValueA] [decimal](18, 2) NULL,
[ValueB] [decimal](18, 2) NULL
CONSTRAINT [PrimaryKey_e149e28f-5754-4229-be01-65fafeebce16] PRIMARY KEY CLUSTERED
(
[TimeStamp] DESC,
[InverterID] ASC
) WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF
, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON
, ALLOW_PAGE_LOCKS = ON)
)
Run Code Online (Sandbox Code Playgroud)
和Index
这样的:
CREATE NONCLUSTERED INDEX [TimeStamp_Power-NonClusteredIndex] ON [dbo].[InverterData]
(
[InverterID] ASC,
[TimeStamp] ASC
)
INCLUDE
(
[ValueA],
[ValueB]
)
Run Code Online (Sandbox Code Playgroud)
该[InverterData]
表具有以下存储统计信息:
使用我描述的模式(加上一些对我的问题不重要的额外表),我可以运行超快速查询,如下所示:
SELECT [TimeStamp], [ValueA], [ValueB]
FROM [InverterData]
JOIN [Inverter] ON [Inverter].[ID] = [InverterData].[InverterID]
JOIN [DataLogger] ON [DataLogger].[ID] = [Inverter].[DataLoggerID]
WHERE [DataLogger].[ProjectID] = 20686
AND [InverterData].[TimeStamp] >= '20160108'
AND [InverterData].[TimeStamp] < '20160109'
Run Code Online (Sandbox Code Playgroud)
执行时间跨度: 178ms
执行计划:
我现在想SELECT GROUP BY
通过 [InverterID] 和 [TimeStamp] 的 15 分钟间隔制作一个。
有这样的想法:
SELECT [InverterID]
, DATEADD(MINUTE, DATEDIFF(MINUTE, 0, [TimeStamp] ) / 15 * 15, 0) AS [TimeStamp]
, SUM([ValueA]), SUM([ValueB])
FROM [InverterData]
JOIN [Inverter] ON [Inverter].[ID] = [InverterData].[InverterID]
JOIN [DataLogger] ON [DataLogger].[ID] = [Inverter].[DataLoggerID]
WHERE [DataLogger].[ProjectID] = 20686
AND [InverterData].[TimeStamp] >= '20160107'
AND [InverterData].[TimeStamp] < '20160108'
GROUP BY
[InverterID], DATEADD(MINUTE, DATEDIFF(MINUTE, 0, [InverterData].[TimeStamp] ) / 15 * 15, 0)
Run Code Online (Sandbox Code Playgroud)
执行时间跨度: 4637ms
执行计划:
我认为这可能与Sort
此处的必要操作有关:
据我所知,可以SORT
通过创建匹配的索引器来避免这种情况。但我不知道如何用我的 15 分钟间隔分组来做到这一点。
正如您所看到的,执行时间跨度SELECT GROUP BY
要长得多。但我不知道在哪里以及如何避免瓶颈!?
如果可能的话,我想有一个更快的解决方案,我可以灵活地更改间隔(例如 10、15 或 6o 分钟)。所以没有计算列。
您可以向表中添加计算列并根据计算构建索引。
例如,该表将是:
CREATE TABLE dbo.InverterData
(
InverterID bigint NOT NULL
, TS datetime NOT NULL
, ValueA decimal(18, 2) NULL
, ValueB decimal(18, 2) NULL
, TS15 AS (DATEADD(MINUTE, DATEDIFF(MINUTE, 0, TS ) / 15 * 15, 0))
, CONSTRAINT PK_InverterData PRIMARY KEY CLUSTERED
(
TS DESC,
InverterID ASC
) WITH (
PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF
, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON
, ALLOW_PAGE_LOCKS = ON
)
);
Run Code Online (Sandbox Code Playgroud)
我已将TimeStamp
列的名称修改为TS
因为TimeStamp
是保留字。
看了保罗的评论后;我认为一个好的索引可能是:
CREATE INDEX IX_InverterData_ID_TS151
ON dbo.InverterData (TS15, InverterID);
Run Code Online (Sandbox Code Playgroud)
如果您将WHERE
子句更改为对索引时间戳列进行操作,则计划中没有排序。我认为对where
条款的更改可能不是问题,因为您似乎整天都在选择。
SELECT InverterData.InverterID
, InverterData.TS15
FROM InverterData
WHERE InverterData.TS15 >= '20140107'
AND InverterData.TS15 < '20160108'
GROUP BY InverterData.InverterID
, InverterData.TS15
OPTION (RECOMPILE);
Run Code Online (Sandbox Code Playgroud)
当然,我没有在示例中包含其他表,因为您没有提供这些详细信息。
上面查询的计划是,我的示例表中有 100,000 行:
说了这么多,如果没有实际的表定义,包括分区方案等,很难说这对你来说实际效果如何。
由于添加(非持久化)计算列只是元数据操作,因此修改表应该几乎是即时的。当您知道没有其他事务(或尽可能少)发生时,您仍然可能想要这样做,因此所需的架构锁(虽然非常短暂)不会阻塞。修改表的DDL是:
ALTER TABLE dbo.InverterData
ADD TS15 AS (DATEADD(MINUTE, DATEDIFF(MINUTE, 0, TS ) / 15 * 15, 0));
Run Code Online (Sandbox Code Playgroud)
归档时间: |
|
查看次数: |
124 次 |
最近记录: |