J'ai une situation où j'ai besoin de pouvoir voir si une personne donnée est dans une hiérarchie utilisateur / gestionnaire.
Je dois pouvoir le faire pour un ensemble d'utilisateurs contre un ensemble de règles (DON 't vous soucier de cela, mais juste pour lui donner un peu de contexte) p>
Idéalement, j'aimerais utiliser un CTE récursif dans une sous-requête corrélée sur la clause WHERE. P>
Mais cela pose beaucoup de problèmes .. p>
Je pense qu'est-ce que j'essaie de faire est: p> est là quand même de faire la définition d'ancrage dynamique en mode de compatibilité 80? Ou une approche alternative? P> p>
3 Réponses :
Combien de niveaux de hiérarchie peut-il y avoir dans la table code> code>? Je m'attendrais à ce qu'il soit raisonnablement bas. Je me demande s'il est assez faible d'essayer plusieurs tests code> imbriqués code>, comme celui-ci:
⦠/* your main query here */ WHERE ⦠AND EXISTS ( SELECT * FROM [Users] u1 WHERE [UserID] = @UserID AND ( [ManagerId] = [Rules].[RuleAddedByUserId] OR EXISTS ( SELECT * FROM [Users] u2 WHERE [UserID] = u1.[ManagerID] AND ( [ManagerId] = [Rules].[RuleAddedByUserId] OR EXISTS ( SELECT * FROM [Users] u3 WHERE [UserID] = u2.[ManagerID] AND ( [ManagerId] = [Rules].[RuleAddedByUserId] OR EXISTS ( ⦠/* and so on, until you've covered all possible levels */ ) ) ) ) ) ) )
Merci pour l'aide, malheureusement, je ne peux pas garantir le niveau de nidification et je dois le faire travailler dans n'importe quel scénario de nidification, mais merci pour le poste.
Une façon de le faire serait de créer un CTE récursif qui a, pour chaque utilisateur, une ligne pour chaque ancêtre de cet utilisateur dans l'arbre. Ensuite, vous pouvez utiliser le CTE pour filtrer pour les ancêtres. Par exemple, avec cet arbre:
CREATE TABLE Users( UserId int NOT NULL PRIMARY KEY, Name nvarchar(25), ManagerId int ); GO INSERT INTO Users (UserId, Name, ManagerId) SELECT 1, 'Bob', NULL UNION ALL SELECT 2, 'Steve', 1 UNION ALL SELECT 3, 'Chris', 2 UNION ALL SELECT 4, 'Alice', 1 UNION ALL SELECT 5, 'Roger', 4 UNION ALL SELECT 6, 'Tony', 5; GO WITH all_ancestors AS ( SELECT u.UserId, u.Name, u.ManagerId AS AncestorId, 1 AS level FROM Users AS u UNION ALL SELECT alla.UserId, alla.Name, u.ManagerId AS AncestorId, alla.level + 1 FROM all_ancestors AS alla INNER JOIN Users AS u ON alla.AncestorId = u.UserId ) SELECT u.* FROM Users AS u INNER JOIN all_ancestors AS a ON u.UserId = a.UserId WHERE a.AncestorId = 4; -- Alice GO DROP TABLE Users; GO
Merci pour l'aide Cheran, ce qui vous intéresse un peu l'approche que j'ai prise. +1 De moi, je posterai ma solution dans quelques minutes.
Je cherchais quelque chose de similaire, mais mes pensées ont finalement pris un tour différent et je n'étais pas satisfait de ce que j'ai trouvé alors. Votre solution est vraiment belle.
Je suis arrivé là-bas finalement! Merci les gars pour l'aide.
Voici un extrait de la SQL que je travaille sur. P>
Je devais juste changer ma pensée, au lieu de voir si un utilisateur existe sous un gestionnaire dans le lieu où clause. J'avais besoin de considérer le CTE un pré-filtre et de construire tous les détails dont j'avais besoin avant la main, puis faites mon filtrage normal après les déclarations existantes (NB que je n'ai pas inclus celles de la brièveté). P>
RulesUserHierarchy(UserId, ManagerId, PushRuleId, OnlyForSubOrdinates) -- Gets only subordinates for rules created by managers. And all users for those created by admin. AS ( --Anchor Definition SELECT [Users].[UserId] ,[Users].[ManagerId] ,[RulesAnchor].[PushRuleId] ,[RulesAnchor].[OnlyForSubOrdinates] FROM [Users] CROSS JOIN [Rules] [RulesAnchor] --assume every user is doing every rule at this point (because the recursive statement has to be the first statement), we'll filter later. WHERE (([OnlyForSubOrdinates]) = 0 OR ([OnlyForSubOrdinates] = 1 AND [UserId] = [RulesAnchor].[AddedByUserId])) UNION ALL --Recursive Member definiation SELECT [Users].[UserId] ,[Users].[ManagerId] ,[RulesUserHierarchy].[PushRuleId] ,[RulesUserHierarchy].[OnlyForSubOrdinates] FROM [Users] INNER JOIN [RulesUserHierarchy] ON [Users].[ManagerId] = [RulesUserHierarchy].[UserId] --recursive hook AND [RulesUserHierarchy].[OnlyForSubOrdinates] = 1 -- no point recursing if it's for everyone, as the anchor will pull back everything for us. WHERE [Users].[UserId] <> [Users].[ManagerId] --don't recurse if the anchor definition matches itself (to avoid an infinate loop). ) -- simple statement to test recursion above, will be filtering the inclusions here (e.g. the other mega exists statements) SELECT [UserId], [ManagerId], [PushRuleId], [OnlyForSubOrdinates] FROM [RulesUserHierarchy]
Et comment la partie d'ancrage ne suffit-elle pas suffisante pour vous dans le prédicat existant? Je ne peux pas voir le point de recouvrer davantage de l'ancre si la partie récursive n'est pas corrélée à la requête extérieure, mais seule la partie d'ancrage est. Qu'est-ce que je rate?
Salut @andriym merci de jeter un coup d'œil. Le problème est que la déclaration "avec" ne peut pas être à l'intérieur d'une sous-requête du tout, je ne peux donc pas lier à une requête extérieure (mon code ci-dessus n'est que pseudo-code - désolé je devrais de mention que). La bonne nouvelle est que je pense que je suis sur une solution, je le posterai une fois que j'ai fini.
Oui, je pense avoir ce droit la première fois. Vous voulez quelque chose comme une requête récursive à l'intérieur
Existe code>, mais
avec code>, utilisé pour la construction de requêtes récursives, ne peut pas être utilisé dans les sous-requêtes, et vous posez donc cette question. Est-ce correct? Supposons que vous ayez autorisé à avoir
avec code> dans les sous-requêtes et ont donc pu construire une sous-requête récursive corrélée. Pourquoi auriez-vous besoin d'avoir une récursion si ce n'est que la partie d'ancrage qui est corrélée à la requête extérieure? Si l'ancrage a renvoyé des lignes, cela suffirait au prédicat existant pour retourner
true code>, et similaire pour
false code>.
Ainsi, la ligne de fond est que vous n'avez probablement pas besoin de récursions et, par conséquent Vous n'avez pas besoin d'utiliser
avec code> i>. Cela signifie, à son tour, vous pouvez avoir votre sous-requête corrélée sous la forme d'un simple
Sélectionnez [UserID], [ManagementId] à partir de [Utilisateurs] où [Managerid] = [Règles]. [Publierdedbyuserid] Code> . (Ou
SELECT * à partir de [Utilisateurs] où [Managerid] = [Règles]. [Diffeddedbyuserid] code>, car la liste des colonnes réelle n'a pas d'importance pour le prédicat existant.)
Merci pour les commentaires @andriym, je vois maintenant ce que vous voulez dire. Mon exemple essayait de reproduire une question que j'ai avec une plus grande requête. Regardez-le maintenant, je ne pense pas que mon exemple est très bon. Ce que j'essaie d'atteindre est de déterminer si un utilisateur est inférieur à un gestionnaire dans une hiérarchie donnée ... Je vais essayer de mettre à jour ma question avec un exemple plus précis. Je dois admettre que je trouve cela délicat d'expliquer ce que je suis après, car le problème est assez épique. Merci encore pour l'aide, je vais essayer de le mettre à jour bientôt.