Je cherche à stocker une liste triée dans une base de données. Je souhaite effectuer efficacement les opérations suivantes.
La méthode simple à laquelle je pourrais penser est de stocker une sorte d'attribut de "rang" dans la table et d'interroger en triant cet attribut. Mais dans cette méthode, insérer/modifier un enregistrement avec un rang devient une opération coûteuse. Existe-t-il une meilleure méthode?
Plus précisément, je cherche à implémenter la table en utilisant SimpleDB d'Amazon. Mais une réponse générale pour une base de données relationnelle devrait également être utile.
Mise à jour du profil de charge:
Étant donné que je prévois cela pour une application Web, cela dépend du nombre d'utilisateurs qui utilisent l'application.
S'il y a 100 000 utilisateurs actifs (super optimisme: P), alors mon estimation très approximative par jour serait
500 000 sélections, 100 000 insertions et suppressions, 500 000 mises à jour
Je m'attendrais à ce que la table atteigne 500k au total.
Je cherche à optimiser les mises à jour, les insertions et les opérations de comparaison. Le rang des articles changera constamment et je dois garder la table à jour.
Si le rang n'est pas complètement arbitraire mais est dérivé à la place d'une autre propriété (par exemple nom, score du joueur, etc.), alors jetez un coup d'œil à réponse de Joel .
Si elle est une propriété arbitraire de vos données, alors cela devrait être stocké sous forme de colonne dans votre table d'enregistrements. En supposant que SimpleDB d'Amazon est similaire au SGBDR typique, vous pouvez ensuite indexer cette colonne et satisfaire rapidement toutes vos requêtes ci-dessus avec la stratégie d'indexation appropriée. Ceci est normal pour un SGBDR.
Étant donné que vous vous attendez à une activité d'insertion et de mise à jour élevée, mais également à une activité de lecture relativement élevée, je recommande de procéder comme suit:
INCLUDE
- classement, ou enregistrez simplement si vous avez groupé sur le classement) satisferait la requête 7.FILLFACTOR
dans SQL Server). Ceci est particulièrement important si vous vous regroupez sur le rang.Si vous vous attendez à 100K + lectures sur une table de taille 100K +, je ne recommande pas d'utiliser l'approche de liste liée. Il ne s'adaptera pas bien à ces tailles.
J'utilise généralement la méthode de "classement" que vous décrivez. Plutôt que de jouer avec la mise à jour des lignes lorsque les éléments doivent être réorganisés, j'ai souvent pu éviter de supprimer tous les enregistrements de la liste et de réinsérer de nouveaux éléments dans le bon ordre. Cette méthode est clairement optimisée pour la récupération.
Une autre approche consisterait à modéliser les enregistrements sous forme de liste chaînée en utilisant une colonne de clé étrangère réflexive "prédécesseur" sur la table:
ID setID item predecessor
--- ------ ------ ------------
1 1 Apple null
2 1 Orange 1
3 2 Cucumber null
4 1 Pear 2
5 1 Grape 4
6 2 Carrot 3
Vous pouvez facilement récupérer une liste et ajouter et supprimer des éléments avec peu de frais généraux, mais il sera difficile de récupérer les enregistrements dans le bon ordre. Il existe peut-être un moyen intelligent de le faire dans une seule requête, probablement avec de nombreuses jointures de table aliasées.
J'utilise cette dernière approche souvent lorsque je modélise une relation arborescente (catégories, dossiers, ensembles et sous-ensembles). J'ai généralement eu une fonction récursive d'une certaine sorte pour reconstruire l'arborescence complète dans mon application.
Je pense que la chose à faire est de stocker la propriété ou les propriétés qui sont utilisées pour calculer le classement puis construire un index sur eux. Plutôt que d'essayer de forcer la base de données à stocker physiquement les données dans l'ordre de classement ou d'utiliser une liste de liens gérée manuellement, pourquoi ne pas laisser le moteur de base de données faire ce pour quoi il a été conçu?
Ce sont les limites d'un non-SGBDR comme simpleDB. Les fonctionnalités dont vous avez besoin ne peuvent pas être implémentées côté DB dans simpleDB, elles doivent être implémentées depuis le côté programmation/application.
Pour un SGBDR comme SQL server
, les fonctionnalités dont vous avez besoin sont rudimentaires à l'index clusterisé.
Avant (x, n) - Renvoie les enregistrements "n" précédant l'enregistrement x dans la liste triée. > Sélectionnez les n premiers résultats où x est inférieur à la valeur et classez par clause.
Après (x, n) - Renvoie les enregistrements "n" succédant à l'enregistrement x dans la liste triée. > Sélectionnez les n premiers résultats où x est supérieur à la valeur et classez par clause.
First (n) - Renvoie les premiers 'n' enregistrements de la liste triée. > Sélectionnez les n premiers résultats.
Last (n) - Renvoie les derniers 'n' enregistrements de la liste triée. > Sélectionnez les n premiers résultats après ordre par desc.
Voici ce que j'ai utilisé pour re-classer ma table Postgres après chaque insertion:
CREATE OR REPLACE FUNCTION re_rank_list() RETURNS trigger AS $re_rank_list$
DECLARE
temprow record;
row_idx integer := 1;
BEGIN
FOR temprow IN
SELECT * FROM your_schema.your_list WHERE list_id = NEW.list_id ORDER BY rank ASC
LOOP
UPDATE your_schema.your_list SET rank = row_idx * 100 WHERE id = temprow.id;
row_idx := row_idx + 1;
END LOOP;
RETURN NEW;
END;
$re_rank_list$ LANGUAGE plpgsql;
CREATE TRIGGER re_rank_list AFTER UPDATE ON your_schema.your_list_value
FOR EACH ROW
WHEN (pg_trigger_depth() = 0)
EXECUTE PROCEDURE re_rank_list();
Pour mon cas d'utilisation, les performances ne sont pas un problème, mais il est important d'avoir confiance qu'elles ne se briseront jamais ou n'agiront pas bizarrement.