J'ai les deux structures de données.
D'abord, une liste de propriétés appliquées à l'objet triples:
Object1 Object2 Object3 Property Value
O1 O2 O3 P1 "abc"
O1 O2 O3 P2 "xyz"
O1 O3 O4 P1 "123"
O2 O4 O5 P1 "098"
Deuxièmement, un arbre d'héritage:
O1
O2
O4
O3
O5
Ou est perçu comme une relation:
Object Parent
O2 O1
O4 O2
O3 O1
O5 O3
O1 null
La sémantique de cet être que O2 hérite des propriétés de O1; O4 - de O2 et O1; O3 - de O1; et O5 - de O3 et O1, dans l'ordre de préséance.
NOTE 1: j'ai un moyen efficace pour sélectionner tous les enfants ou tous les parents d'un objet donné. C'est actuellement mis en œuvre avec la gauche et la droite, index, mais hierarchyid pourrait également fonctionner. Cela ne semble pas important maintenant.
NOTE 2: j'ai tiggers en place assurez-vous que "l'Objet" de la colonne contient toujours tous les objets possibles, même quand ils n'ont pas vraiment d'être là (c'est à dire n'ont pas de parent ou enfants). Cela rend possible l'utilisation d' inner join
s plutôt que sévèrement moins effiecient outer join
s.
L'objectif est: étant Donné une paire de (Propriété, Valeur), le retour de tous les objets triples qui ont cette propriété avec la valeur soit définie de manière explicite ou héritées d'un parent.
NOTE 1: Un objet triple (X,Y,Z)
est considéré comme un "parent" de triple (A,B,C)
quand il est vrai que soit X = A
ou X is a parent of A
, et la même chose est vraie pour (Y,B)
et (Z,C)
.
NOTE 2: UNE propriété définie sur un plus proche parent "remplace" la même propriété définie sur un plus lointain parent.
REMARQUE 3: Lorsque (A,B,C) a deux parents, (X1,Y1,Z1) et (X2,Y2,Z2), alors (X1,Y1,Z1) est considéré comme un "plus proche parent" lorsque:
(a) X2 est un parent de X1, ou
(b) X2 = X1 et Y2 est un parent de Y1, ou
(c) X2 = X1 et Y2 = Y1 et Z2 est un parent de Z1
En d'autres termes, la "proximité" dans l'ascendance pour les triplets est définie sur la base des premiers éléments de triples tout d'abord, puis sur la deuxième composants, puis sur la troisième composante. Cette règle établit une unambigous commande partielle de triples dans les termes de l'ascendance.
Par exemple, pour la paire de (P1, "abc"), l'ensemble de résultats de triples seront:
O1, O2, O3 -- Defined explicitly
O1, O2, O5 -- Because O5 inherits from O3
O1, O4, O3 -- Because O4 inherits from O2
O1, O4, O5 -- Because O4 inherits from O2 and O5 inherits from O3
O2, O2, O3 -- Because O2 inherits from O1
O2, O2, O5 -- Because O2 inherits from O1 and O5 inherits from O3
O2, O4, O3 -- Because O2 inherits from O1 and O4 inherits from O2
O3, O2, O3 -- Because O3 inherits from O1
O3, O2, O5 -- Because O3 inherits from O1 and O5 inherits from O3
O3, O4, O3 -- Because O3 inherits from O1 and O4 inherits from O2
O3, O4, O5 -- Because O3 inherits from O1 and O4 inherits from O2 and O5 inherits from O3
O4, O2, O3 -- Because O4 inherits from O1
O4, O2, O5 -- Because O4 inherits from O1 and O5 inherits from O3
O4, O4, O3 -- Because O4 inherits from O1 and O4 inherits from O2
O5, O2, O3 -- Because O5 inherits from O1
O5, O2, O5 -- Because O5 inherits from O1 and O5 inherits from O3
O5, O4, O3 -- Because O5 inherits from O1 and O4 inherits from O2
O5, O4, O5 -- Because O5 inherits from O1 and O4 inherits from O2 and O5 inherits from O3
Notez que le triple (O2, O4, O5) est absent de cette liste. C'est parce que la propriété P1 est définie de manière explicite pour le triple (O2, O4, O5) et cela empêche que le triple d'hériter de la propriété de (O1, O2, O3). Notez également que le triple (O4, O4, O5) est également absent. C'est parce que triple hérite de sa valeur de P1="098" de l' (O2, O4, O5), parce que c'est un proche parent de (O1, O2, O3).
La façon simple de le faire est la suivante. Tout d'abord, pour chaque triplet qu'une propriété est définie sur, sélectionnez tous les possibles enfant triples:
select Children1.Id as O1, Children2.Id as O2, Children3.Id as O3, tp.Property, tp.Value
from TriplesAndProperties tp
-- Select corresponding objects of the triple
inner join Objects as Objects1 on Objects1.Id = tp.O1
inner join Objects as Objects2 on Objects2.Id = tp.O2
inner join Objects as Objects3 on Objects3.Id = tp.O3
-- Then add all possible children of all those objects
inner join Objects as Children1 on Objects1.Id [isparentof] Children1.Id
inner join Objects as Children2 on Objects2.Id [isparentof] Children2.Id
inner join Objects as Children3 on Objects3.Id [isparentof] Children3.Id
Mais ce n'est pas toute l'histoire: si certaines triple hérite de la même propriété de plusieurs parents, cette requête donnera des résultats contradictoires. Donc, la deuxième étape consiste à sélectionner l'une de ces résultats contradictoires:
select * from
(
select
Children1.Id as O1, Children2.Id as O2, Children3.Id as O3, tp.Property, tp.Value,
row_number() over(
partition by Children1.Id, Children2.Id, Children3.Id, tp.Property
order by Objects1.[depthInTheTree] descending, Objects2.[depthInTheTree] descending, Objects3.[depthInTheTree] descending
)
as InheritancePriority
from
... (see above)
)
where InheritancePriority = 1
La fenêtre de la fonction row_number() over( ... )
est le suivant: pour chaque combinaison unique d'objets triple et de la propriété, il trie toutes les valeurs ancestrales de la distance à partir de la triple pour les parents que la valeur est héritée de, et puis je ne sélectionnez la première de la liste de valeurs.
Un effet similaire peut être obtenu avec un GROUP BY
et ORDER BY
des déclarations, mais je viens de trouver la fonction de fenêtre sémantiquement plus propres (les plans d'exécution de leurs résultats soient identiques).
Le point est, j'ai besoin de choisir le plus proche de contribuer ancêtres, et pour cela j'ai besoin de les grouper et de les trier au sein du groupe.
Et enfin, maintenant, je peux simplement filtrer le résultat d'une Propriété et de la Valeur.
Ce schéma fonctionne. Très fiable et prévisible. Il s'est avéré être très puissant pour la gestion des tâches qu'il implémente.
Le seul problème est, il est awfuly lent.
On pourrait dire à la jointure de sept tables peut-être ralentir les choses, mais n'est en réalité pas le goulot d'étranglement.
Selon le plan d'exécution j'obtiens à partir de SQL Management Studio (ainsi que le générateur de profils SQL), le goulot d'étranglement est le tri.
Le problème est, afin de satisfaire ma fenêtre de fonction, le serveur a de trier par Children1.Id, Children2.Id, Children3.Id, tp.Property, Parents1.[depthInTheTree] descending, Parents2.[depthInTheTree] descending, Parents3.[depthInTheTree] descending
, et il n'existe pas d'indices qu'il peut utiliser, car les valeurs proviennent d'une jointure croisée de plusieurs tables.
EDIT: Par Michael Buen suggestion (merci Michel), j'ai posté le puzzle entier à sqlfiddle ici. On peut voir dans le plan de l'exécution de l'opération de Tri représentent 32% de la totalité de la requête, et qui va croître avec le nombre total de lignes, car toutes les autres opérations d'utiliser les index.
Habituellement dans de tels cas, je voudrais utiliser une vue indexée, mais pas dans ce cas, parce que les vues indexées ne peut pas contenir de l'auto-jointures, dont il y en a six.
La seule façon que je peux penser à ce jour est de créer des six exemplaires de la table d'Objets et de les utiliser pour la rejoint, permettant ainsi une vue indexée.
N'a que le temps est venu que je doit en être réduit à ce genre de hacks? Le désespoir s'installe.