Tengo una tabla de datos SQL con la siguiente estructura:
CREATE TABLE Data(
Id uniqueidentifier NOT NULL,
Date datetime NOT NULL,
Value decimal(20, 10) NULL,
RV timestamp NOT NULL,
CONSTRAINT PK_Data PRIMARY KEY CLUSTERED (Id, Date)
)
El número de identificadores distintos varía de 3000 a 50000.
El tamaño de la tabla varía hasta más de mil millones de filas.
One Id puede cubrir entre unas pocas filas hasta el 5% de la tabla.
La consulta más ejecutada en esta tabla es:
SELECT Id, Date, Value, RV
FROM Data
WHERE Id = @Id
AND Date Between @StartDate AND @StopDate
Ahora tengo que implementar la recuperación incremental de datos en un subconjunto de ID, incluidas las actualizaciones.
Luego usé un esquema de solicitud en el que la persona que llama proporciona una versión de fila específica, recupera un bloque de datos y usa el valor máximo de versión de fila de los datos devueltos para la llamada posterior.
He escrito este procedimiento:
CREATE TYPE guid_list_tbltype AS TABLE (Id uniqueidentifier not null primary key)
CREATE PROCEDURE GetData
@Ids guid_list_tbltype READONLY,
@Cursor rowversion,
@MaxRows int
AS
BEGIN
SELECT A.*
FROM (
SELECT
Data.Id,
Date,
Value,
RV,
ROW_NUMBER() OVER (ORDER BY RV) AS RN
FROM Data
inner join (SELECT Id FROM @Ids) Ids ON Ids.Id = Data.Id
WHERE RV > @Cursor
) A
WHERE RN <= @MaxRows
END
Donde @MaxRows
oscilará entre 500,000 y 2,000,000 dependiendo de qué tan fragmentado el cliente quiera sus datos.
He intentado diferentes enfoques:
- Indización en (Id, RV):
CREATE NONCLUSTERED INDEX IDX_IDRV ON Data(Id, RV) INCLUDE(Date, Value);
Utilizando el índice, la consulta busca las filas en las que RV = @Cursor
para cada uno Id
de @Ids
, leen las siguientes filas a continuación, se funden el resultado y tipo.
La eficiencia depende entonces de la posición relativa del @Cursor
valor.
Si está cerca del final de los datos (ordenado por RV), la consulta es instantánea y, si no, la consulta puede demorar hasta minutos (nunca permita que se ejecute hasta el final).
El problema con este enfoque es que @Cursor
está cerca del final de los datos y el orden no es doloroso (ni siquiera es necesario si la consulta devuelve menos filas que @MaxRows
) o está más atrás y la consulta tiene que ordenar las @MaxRows * LEN(@Ids)
filas.
- Indexación en RV:
CREATE NONCLUSTERED INDEX IDX_RV ON Data(RV) INCLUDE(Id, Date, Value);
Usando el índice, la consulta busca la fila donde RV = @Cursor
luego lee cada fila descartando los Ids no solicitados hasta que llegue @MaxRows
.
La eficiencia entonces depende del% de Ids solicitados ( LEN(@Ids) / COUNT(DISTINCT Id)
) y su distribución.
Más% Id solicitado significa menos filas descartadas, lo que significa lecturas más eficientes,% Id menos solicitado significa más filas descartadas, lo que significa más lecturas para la misma cantidad de filas resultantes.
El problema con este enfoque es que si los ID solicitados contienen solo unos pocos elementos, es posible que tenga que leer todo el índice para obtener las filas deseadas.
- Uso de índice filtrado o vistas indizadas
CREATE NONCLUSTERED INDEX IDX_RVClient1 ON Data(Id, RV) INCLUDE(Date, Value)
WHERE Id IN (/* list of Ids for specific client*/);
O
CREATE VIEW vDataClient1 WITH SCHEMABINDING
AS
SELECT
Id,
Date,
Value,
RV
FROM dbo.Data
WHERE Id IN (/* list of Ids for specific client*/)
CREATE UNIQUE CLUSTERED INDEX IDX_IDRV ON vDataClient1(Id, Rv);
Este método permite una indexación perfectamente eficiente y planes de ejecución de consultas, pero viene con desventajas: 1. Prácticamente, tendré que implementar SQL dinámico para crear los índices o vistas y modificar el procedimiento de solicitud para usar el índice o vista correctos. 2. Tendré que mantener un índice o vista por cliente existente, incluido el almacenamiento. 3. Cada vez que un cliente tendrá que modificar su lista de ID solicitados, tendré que soltar el índice o la vista y volver a crearlo.
Parece que no puedo encontrar un método que se adapte a mis necesidades.
Estoy buscando mejores ideas para implementar la recuperación incremental de datos. Esas ideas podrían implicar reelaborar el esquema de solicitud o el esquema de la base de datos, aunque preferiría un mejor enfoque de indexación si existe.
Value
columna. @crokusek: No ordenar por RV, ID en lugar de RV solo aumentará la carga de trabajo de clasificación sin ningún beneficio, no entiendo el razonamiento detrás de su comentario. Por lo que he leído, RV debería ser único a menos que inserte datos específicamente en esa columna, lo que no hace la aplicación.