Eu tenho uma tabela de dados SQL com a seguinte estrutura:
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)
)
O número de IDs distintos varia de 3000 a 50000.
O tamanho da tabela varia até mais de um bilhão de linhas.
Um ID pode cobrir entre algumas linhas até 5% da tabela.
A única consulta mais executada nesta tabela é:
SELECT Id, Date, Value, RV
FROM Data
WHERE Id = @Id
AND Date Between @StartDate AND @StopDate
Agora preciso implementar a recuperação incremental de dados em um subconjunto de IDs, incluindo atualizações.
Em seguida, usei um esquema de solicitação no qual o chamador fornece uma versão específica da linha, recupera um bloco de dados e usa o valor máximo da versão da linha dos dados retornados para a chamada subsequente.
Eu escrevi este procedimento:
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
Onde @MaxRows
variará entre 500.000 e 2.000.000, dependendo de como o cliente desejar os dados.
Eu tentei abordagens diferentes:
- Indexação em (Id, RV):
CREATE NONCLUSTERED INDEX IDX_IDRV ON Data(Id, RV) INCLUDE(Date, Value);
Usando o índice, a consulta buscar as linhas onde RV = @Cursor
para cada Id
no @Ids
, leia as seguintes linhas em seguida, mesclar o resultado e classificar.
A eficiência depende então da posição relativa do @Cursor
valor.
Se estiver próximo do final dos dados (ordenado por RV), a consulta será instantânea e, se não, a consulta poderá levar alguns minutos (nunca deixe que ela seja executada até o final).
o problema dessa abordagem é que ele @Cursor
está próximo do final dos dados e a classificação não é dolorosa (nem mesmo necessária se a consulta retornar menos linhas do que @MaxRows
) ou está mais atrasada e a consulta precisa classificar as @MaxRows * LEN(@Ids)
linhas.
- Indexação no RV:
CREATE NONCLUSTERED INDEX IDX_RV ON Data(RV) INCLUDE(Id, Date, Value);
Usando o índice, a consulta procura a linha onde RV = @Cursor
, em seguida, lê todas as linhas descartando os IDs não solicitados até atingir @MaxRows
.
A eficiência depende da% de IDs solicitados ( LEN(@Ids) / COUNT(DISTINCT Id)
) e de sua distribuição.
Id% mais solicitado significa menos linhas descartadas, o que significa leituras mais eficientes, menos% Id id solicitado significa mais linhas descartadas, o que significa mais leituras para a mesma quantidade de linhas resultantes.
O problema dessa abordagem é que, se os IDs solicitados contiverem apenas alguns elementos, talvez seja necessário ler o índice inteiro para obter as linhas desejadas.
- Usando índice filtrado ou visualizações indexadas
CREATE NONCLUSTERED INDEX IDX_RVClient1 ON Data(Id, RV) INCLUDE(Date, Value)
WHERE Id IN (/* list of Ids for specific client*/);
Ou
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);
Esse método permite planos de execução de consulta e indexação perfeitamente eficientes, mas apresenta desvantagens: 1. Praticamente, terei que implementar o SQL dinâmico para criar índices ou visualizações e modificar o procedimento de solicitação para usar o índice ou visualização correta. 2. Terei que manter um índice ou visualização pelo cliente existente, incluindo armazenamento. 3. Toda vez que um cliente precisar modificar sua lista de IDs solicitados, terei que eliminar o índice ou visualizar e recriá-lo.
Não consigo encontrar um método que atenda às minhas necessidades.
Estou procurando idéias melhores para implementar a recuperação incremental de dados. Essas idéias podem implicar uma reformulação do esquema solicitante ou do esquema do banco de dados, embora eu prefira uma abordagem de indexação melhor, se houver.
Value
coluna. @ crokusek: Não pedir por RV, ID em vez de RV apenas aumenta a carga de trabalho de classificação sem nenhum benefício, não entendo o raciocínio por trás do seu comentário. Pelo que li, o RV deve ser único, a menos que insira dados especificamente nessa coluna, o que o aplicativo não faz.