设定
我有一个约115,382,254行的巨大表。该表相对简单,并且记录了应用程序的处理操作。
CREATE TABLE [data].[OperationData](
[SourceDeciveID] [bigint] NOT NULL,
[FileSource] [nvarchar](256) NOT NULL,
[Size] [bigint] NULL,
[Begin] [datetime2](7) NULL,
[End] [datetime2](7) NOT NULL,
[Date] AS (isnull(CONVERT([date],[End]),CONVERT([date],'19000101',(112)))) PERSISTED NOT NULL,
[DataSetCount] [bigint] NULL,
[Result] [int] NULL,
[Error] [nvarchar](max) NULL,
[Status] [int] NULL,
CONSTRAINT [PK_OperationData] PRIMARY KEY CLUSTERED
(
[SourceDeviceID] ASC,
[FileSource] ASC,
[End] ASC
))
CREATE TABLE [model].[SourceDevice](
[ID] [bigint] IDENTITY(1,1) NOT NULL,
[Name] [nvarchar](50) NULL,
CONSTRAINT [PK_DataLogger] PRIMARY KEY CLUSTERED
(
[ID] ASC
))
ALTER TABLE [data].[OperationData] WITH CHECK ADD CONSTRAINT [FK_OperationData_SourceDevice] FOREIGN KEY([SourceDeviceID])
REFERENCES [model].[SourceDevice] ([ID])
该表每天约有500个簇。
而且,该表已通过PK很好地索引了,统计数据是最新的,并且INDEXer每晚都受到损坏。
基于索引的SELECT很快,我们对此没有任何问题。
问题
我需要知道的最后(TOP)行,[End]
并按进行分区[SourceDeciveID]
。获得[OperationData]
每个源设备的最后一个。
题
我需要找到一种很好的方法来解决此问题,而又不能使数据库达到极限。
努力1
第一次尝试是显而易见的GROUP BY
或SELECT OVER PARTITION BY
查询。这里的问题也很明显,每个查询都必须扫描非常大的分区顺序/查找第一行。因此查询非常慢,并且对IO的影响很大。
示例查询1
;WITH cte AS
(
SELECT *,
ROW_NUMBER() OVER (PARTITION BY [SourceDeciveID] ORDER BY [End] DESC) AS rn
FROM [data].[OperationData]
)
SELECT *
FROM cte
WHERE rn = 1
查询示例2
SELECT *
FROM [data].[OperationData] AS d
CROSS APPLY
(
SELECT TOP 1 *
FROM [data].[OperationData]
WHERE [SourceDeciveID] = d.[SourceDeciveID]
ORDER BY [End] DESC
) AS ds
失败!
努力2
我创建了一个帮助表,以始终保存对TOP行的引用。
CREATE TABLE [data].[LastOperationData](
[SourceDeciveID] [bigint] NOT NULL,
[FileSource] [nvarchar](256) NOT NULL,
[End] [datetime2](7) NOT NULL,
CONSTRAINT [PK_LastOperationData] PRIMARY KEY CLUSTERED
(
[SourceDeciveID] ASC
)
ALTER TABLE [data].[LastOperationData] WITH CHECK ADD CONSTRAINT [FK_LastOperationData_OperationData] FOREIGN KEY([SourceDeciveID], [FileSource], [End])
REFERENCES [data].[OperationData] ([SourceDeciveID], [FileSource], [End])
为了填充表,创建了一个触发器,如果[End]
插入更高的列,则该触发器将始终添加/更新源行。
CREATE TRIGGER [data].[OperationData_Last]
ON [data].[OperationData]
AFTER INSERT
AS
BEGIN
SET NOCOUNT ON;
MERGE [data].[LastOperationData] AS [target]
USING (SELECT [SourceDeciveID], [FileSource], [End] FROM inserted) AS [source] ([SourceDeciveID], [FileSource], [End])
ON ([target].[SourceDeciveID] = [FileSource].[SourceDeciveID])
WHEN MATCHED AND [target].[End] < [source].[End] THEN
UPDATE SET [target].[FileSource] = source.[FileSource], [target].[End] = source.[End]
WHEN NOT MATCHED THEN
INSERT ([SourceDeciveID], [FileSource], [End])
VALUES (source.[SourceDeciveID], source.[FileSource], source.[End]);
END
这里的问题是,它也具有非常大的IO影响,我不知道为什么。
如您在查询计划中所见,它还对整个[OperationData]
表执行扫描。
失败!
2
在您的第一个代码块中,我看不到聚簇索引的第一列来自何处-是吗?
—
George.Palacios
是的,抱歉,SSMS并未将其包括在
—
Steffen Mangold
CREATE TABLE
脚本中,但是在查询计划中您将看到分区。我将编辑问题。
不是多余的索引,因为
—
Steffen Mangold
PRIMARY KEY CLUSTERED
您认为其中包含的索引可能会有所帮助?
很抱歉,这是一个错误,我更清楚地修改了问题的名称,以进行更正。
—
Steffen Mangold
@ypercubeᵀᴹ是的,因为
—
斯特芬·曼戈尔德
SELECT [SourceID], [Source], [End] FROM inserted
一些如何在上扫描表[OperationData]
。