Preparar
Tengo una gran mesa de ~ 115,382,254 filas. La tabla es relativamente simple y registra las operaciones del proceso de aplicación.
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 tabla está agrupada en alrededor de 500 grupos y en una base diaria.
Además, la tabla está bien indexada por PK, las estadísticas están actualizadas y el INDEXer se defrauda todas las noches.
Los SELECT basados en índices son rápidos como un rayo y no tuvimos ningún problema con eso.
Problema
Necesito saber la última fila (SUPERIOR) [End]
y particionado por [SourceDeciveID]
. Para obtener lo último [OperationData]
de cada dispositivo fuente.
Pregunta
Necesito encontrar una manera de resolver esto de una buena manera y sin llevar el DB a los límites.
Esfuerzo 1
El primer intento fue obvio GROUP BY
o SELECT OVER PARTITION BY
consulta. El problema aquí también es obvio, cada consulta tiene que escanear por orden de partición / encontrar la fila superior. Por lo tanto, la consulta es muy lenta y tiene un impacto de E / S muy alto.
Ejemplo de consulta 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
Ejemplo de consulta 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
¡HA FALLADO!
Esfuerzo 2
Creé una tabla de ayuda para mantener siempre una referencia a la fila SUPERIOR.
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])
Para llenar la tabla, se creó un activador para agregar / actualizar siempre la fila de origen si [End]
se inserta una columna más alta .
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
El problema aquí es que también tiene un gran impacto de IO y no sé por qué.
Como puede ver aquí en el plan de consulta , también ejecuta una exploración en toda la [OperationData]
tabla.
Tiene un gran impacto general en mi base de datos.
¡HA FALLADO!
CREATE TABLE
script, pero dentro del plan de consulta verá las particiones. Editaré la pregunta.
PRIMARY KEY CLUSTERED
que cree que puede ayudar?
SELECT [SourceID], [Source], [End] FROM inserted
alguna manera escanea una tabla en el [OperationData]
.