J'ai une table de données SQL avec la structure suivante:
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)
)
Le nombre d'ID distincts varie de 3000 à 50000.
La taille de la table varie jusqu'à plus d'un milliard de lignes.
Un identifiant peut couvrir entre quelques lignes jusqu'à 5% du tableau.
La requête la plus exécutée sur cette table est:
SELECT Id, Date, Value, RV
FROM Data
WHERE Id = @Id
AND Date Between @StartDate AND @StopDate
Je dois maintenant implémenter la récupération incrémentielle des données sur un sous-ensemble d'ID, y compris les mises à jour.
J'ai ensuite utilisé un schéma de demande dans lequel l'appelant fournit une version de ligne spécifique, récupère un bloc de données et utilise la valeur de version de ligne maximale des données renvoyées pour l'appel suivant.
J'ai écrit cette procédure:
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
Où @MaxRows
se situera entre 500 000 et 2 000 000 selon la façon dont le client voudra ses données.
J'ai essayé différentes approches:
- Indexation sur (Id, RV):
CREATE NONCLUSTERED INDEX IDX_IDRV ON Data(Id, RV) INCLUDE(Date, Value);
À l'aide de l'index, la requête recherche les lignes où RV = @Cursor
pour chaque Id
entrée @Ids
, lit les lignes suivantes, puis fusionne le résultat et trie.
L'efficacité dépend alors de la position relative de la @Cursor
valeur.
Si elle est proche de la fin des données (commandée par RV), la requête est instantanée et sinon la requête peut prendre jusqu'à quelques minutes (ne jamais la laisser s'exécuter jusqu'à la fin).
le problème avec cette approche est qu'il @Cursor
est proche de la fin des données et que le tri n'est pas pénible (même pas nécessaire si la requête retourne moins de lignes que @MaxRows
), soit il est plus loin et la requête doit trier les @MaxRows * LEN(@Ids)
lignes.
- Indexation sur RV:
CREATE NONCLUSTERED INDEX IDX_RV ON Data(RV) INCLUDE(Id, Date, Value);
À l'aide de l'index, la requête recherche la ligne où RV = @Cursor
lire ensuite chaque ligne en rejetant les ID non demandés jusqu'à ce qu'elle atteigne @MaxRows
.
L'efficacité dépend alors du% des identifiants demandés ( LEN(@Ids) / COUNT(DISTINCT Id)
) et de leur distribution.
Plus l'Id% demandé signifie moins de lignes supprimées, ce qui signifie des lectures plus efficaces, moins l'Id% demandé signifie plus de lignes supprimées, ce qui signifie plus de lectures pour la même quantité de lignes résultantes.
Le problème avec cette approche est que si les identifiants demandés ne contiennent que quelques éléments, il peut être nécessaire de lire l'index entier pour obtenir les lignes souhaitées.
- Utilisation d'un index filtré ou de vues indexées
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);
Cette méthode permet une indexation et des plans d'exécution des requêtes parfaitement efficaces, mais présente des inconvénients: 1. Pratiquement, je devrai implémenter du SQL dynamique pour créer les index ou les vues et modifier la procédure de demande pour utiliser le bon index ou la bonne vue. 2. Je devrai maintenir un index ou une vue par client existant, y compris le stockage. 3. Chaque fois qu'un client devra modifier sa liste d'ID demandés, je devrai supprimer l'index ou le visualiser et le recréer.
Je n'arrive pas à trouver une méthode qui convienne à mes besoins.
Je recherche de meilleures idées pour implémenter la récupération incrémentielle des données. Ces idées pourraient impliquer de retravailler le schéma demandeur ou le schéma de base de données bien que je préfère une meilleure approche d'indexation s'il y en a une.
Value
colonne. @crokusek: Ne pas commander par RV, ID au lieu de RV ne fait qu'augmenter la charge de travail de tri sans aucun avantage, je ne comprends pas le raisonnement derrière votre commentaire. D'après ce que j'ai lu, RV devrait être unique à moins d'insérer des données spécifiquement dans cette colonne, ce que l'application ne fait pas.