Impostare
Ho un enorme tavolo di ~ 115.382.254 righe. La tabella è relativamente semplice e registra le operazioni del processo dell'applicazione.
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])
La tabella è raggruppata in circa 500 cluster e su base giornaliera.
Inoltre, la tabella è ben indicizzata da PK, le statistiche sono aggiornate e l'INDEXer viene deframmentato ogni notte.
I SELECT basati sull'indice sono velocissimi e non abbiamo avuto alcun problema.
Problema
Devo conoscere l'ultima riga (TOP) di [End]e partizionata da [SourceDeciveID]. Per ottenere l'ultimo [OperationData]di ogni dispositivo sorgente.
Domanda
Devo trovare un modo per risolverlo in modo positivo e senza portare il DB al limite.
Sforzo 1
Il primo tentativo è stato ovvio GROUP BYo SELECT OVER PARTITION BYinterrogativo. Anche qui il problema è evidente, ogni query deve eseguire la scansione su un ordine di partizione / trovare la riga superiore. Quindi la query è molto lenta e ha un impatto IO molto elevato.
Query di esempio 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
Esempio di query 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
FALLITA!
Sforzo 2
Ho creato una tabella di aiuto per contenere sempre un riferimento alla riga 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])
Per riempire la tabella è stato creato un trigger per aggiungere / aggiornare sempre la riga di origine se [End]è inserita una colonna superiore .
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
Il problema qui è che ha anche un impatto IO enorme e non so perché.
Come puoi vedere qui nel piano delle query , esegue anche una scansione sull'intera [OperationData]tabella.
Ha un enorme impatto complessivo sul mio DB.

FALLITA!
CREATE TABLEscript ma all'interno del piano di query vedrai le partizioni. Modificherò la domanda.
PRIMARY KEY CLUSTEREDte pensi che possa aiutare?
SELECT [SourceID], [Source], [End] FROM insertedalcuni come eseguono la scansione di una tabella su [OperationData].

