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!
fuente
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]
.Respuestas:
Si tiene una tabla de
SourceID
valores y un índice en su tabla principal(SourceID, End) include (othercolumns)
, simplemente useOUTER APPLY
.Si sabe que solo está después de su nueva partición, puede incluir un filtro en End, como
AND d.[End] > DATEADD(day, -1, GETDATE())
Editar: debido a que su índice agrupado está activado
SourceID, Source, End)
, coloque Fuente en su tabla de Fuentes también y únase a eso también. Entonces no necesitas el nuevo índice.fuente
Source
tabla que hace referencia a lasourceID
columna. La fuente de la columna es solo un nombre de archivo. Es un nombre poco confuso. Para cadaSource
dispositivo (sourceID) podría haber solo una entrada única para un archivosource
(columna) en una marca de tiempo. Además, no puedo eliminar la partición porque el más nuevoEnd
está fragmentado ampliamente. Por eso se me ocurrió la solución de activación. Creo que una consulta en vivo no funcionará aquí.