Installieren
Ich habe eine riesige Tabelle mit ~ 115.382.254 Zeilen. Die Tabelle ist relativ einfach und protokolliert Anwendungsprozessvorgänge.
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])
Die Tabelle ist täglich in rund 500 Clustern zusammengefasst.
Außerdem ist die Tabelle von PK gut indiziert, die Statistiken sind aktuell und der INDEXer wird jede Nacht defragmentiert.
Indexbasierte SELECTs sind blitzschnell und wir hatten kein Problem damit.
Problem
Ich muss die letzte (TOP) Zeile von kennen [End]
und durch partitionieren [SourceDeciveID]
. Um das allerletzte [OperationData]
von jedem Quellgerät zu erhalten.
Frage
Ich muss einen Weg finden, dies auf gute Weise zu lösen, ohne die DB an ihre Grenzen zu bringen.
Aufwand 1
Der erste Versuch war offensichtlich GROUP BY
oder SELECT OVER PARTITION BY
fraglich. Das Problem hier ist auch offensichtlich, jede Abfrage muss über sehr Partitionsreihenfolge scannen / die oberste Zeile finden. Die Abfrage ist also sehr langsam und hat eine sehr hohe E / A-Auswirkung.
Beispielabfrage 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
Beispielabfrage 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
GESCHEITERT!
Aufwand 2
Ich habe eine Hilfetabelle erstellt, die immer einen Verweis auf die oberste Zeile enthält.
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])
Um die Tabelle zu füllen, wurde ein Trigger erstellt, der die Quellzeile immer hinzufügt / aktualisiert, wenn eine höhere [End]
Spalte eingefügt wird.
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
Das Problem hierbei ist, dass es auch einen sehr großen Einfluss auf die E / A hat und ich nicht weiß warum.
Wie Sie hier im Abfrageplan sehen können, wird auch ein Scan über die gesamte [OperationData]
Tabelle ausgeführt.
Es hat insgesamt einen enormen Einfluss auf meine DB.
GESCHEITERT!
CREATE TABLE
Skript auf, aber im Abfrageplan werden die Partitionen angezeigt . Ich werde die Frage bearbeiten.
PRIMARY KEY CLUSTERED
Sie glauben, dass er helfen könnte?
SELECT [SourceID], [Source], [End] FROM inserted
einige wie eine Tabelle auf dem scannen [OperationData]
.