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 BY
o SELECT OVER PARTITION BY
interrogativo. 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 TABLE
script ma all'interno del piano di query vedrai le partizioni. Modificherò la domanda.
PRIMARY KEY CLUSTERED
te pensi che possa aiutare?
SELECT [SourceID], [Source], [End] FROM inserted
alcuni come eseguono la scansione di una tabella su [OperationData]
.