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!
quelle
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]
.Antworten:
Wenn Sie eine Wertetabelle
SourceID
und einen Index für Ihre Haupttabelle haben(SourceID, End) include (othercolumns)
, verwenden Sie einfachOUTER APPLY
.Wenn Sie wissen, dass Sie erst nach Ihrer neuesten Partition suchen, können Sie einen Filter für End einfügen, z
AND d.[End] > DATEADD(day, -1, GETDATE())
Bearbeiten: Da Ihr Clustered-Index aktiviert ist
SourceID, Source, End)
, fügen Sie Source ebenfalls in Ihre Sources-Tabelle ein und schließen Sie sich dem ebenfalls an. Dann brauchen Sie den neuen Index nicht.quelle
Source
Tabelle, die auf diesourceID
Spalte verweist . Die Spaltenquelle ist nur ein Dateiname. Es ist ein wenig verwirrend zu benennen. Für jedesSource
Gerät (Quell-ID) kann es nur einen einzigen Eintrag für eine Dateisource
(Spalte) zu einem Zeitstempel geben. Außerdem kann ich keine Partitionseliminierung durchführen, da die neuesteEnd
Version stark fragmentiert ist. Deshalb habe ich mir die Trigger-Lösung ausgedacht. Ich denke, eine Live-Abfrage wird hier nicht funktionieren.