Question

J'ai une question sur la façon d'optimiser une requête. En fait, comme je vais exécuter la requête souvent, je pensais à l'aide d'une vue matérialisée ou indexé (est-ce une bonne idée ici?) Ou dénormalisation.

Considérons les quatre tableaux ci-dessous (avec des champs non pertinents omis):

  • Utilisateurs (int userId)
  • Groupes (int groupId)
  • GroupMemberships (int, int userId groupId, bool isSharing)
  • Ordinateurs (int userId)

Les relations sont qu'un utilisateur peut avoir 0..n ordinateurs (un utilisateur à de nombreux ordinateurs) et peut être un membre de 0..n groupes. Un groupe peut avoir des utilisateurs 0..n (de nombreux utilisateurs à de nombreux groupes). Le « isSharing » indique si un utilisateur partage à ce groupe ou est membre « lecture seule » de ce groupe (qui est, peut voir partager les ordinateurs des membres, mais ne partage pas son propre).

La requête est de trouver, pour un utilisateur donné ce que les ordinateurs que l'utilisateur peut voir. Un utilisateur peut voir tous ses propres ordinateurs. Elle peut également voir tous les ordinateurs d'autres utilisateurs qui sont dans des groupes qu'elle est une MEBER de et partagent à ce groupe. D'accord, cela ne fait pas beaucoup de sens, voici donc l'objectif en O (n ^ 3) psudocode:

List<Computer> l
foreach(Computer c in Computers)
    if(c.userId == current_user_id)
        add c to l
    else
        foreach(GroupMembership m where m.userId == current_user_id)
            foreach(GroupMembership m2 where c.userId == m2.userId && m.groupId == m2.groupId)
                if(m2.isSharing)
                    add c to l

En ce moment je suis en utilisant un mappeur ORM et de faire essentiellement ce qui précède (je ne suis pas trop bien sur le tout SQL), mais qui est évidemment une solution moins qu'idéales. Je index sur tous les domaines qui y sont énumérés, je (sauf IsShared) et un indice supplémentaire sur (userId, groupId) tuple de GroupMembership. Mais toute base de données peut wizards là penser à une meilleure solution?

Le projet est pas encore vivre, mais je suppose qu'il y aurait une moyenne de peut-être 1,2 ordinateurs par utilisateur (tout le monde aura un, quelques-uns peuvent avoir plus) et peut-être 0,75 appartenance à un groupe par utilisateur (de nombreux utilisateurs ne utiliser les groupes disposent, mais ceux qui seront probablement membres de plusieurs groupes). De plus, tous ces tableaux associés recevront des ajouts fréquents, qui peuvent faire des vues matérialisées une solution moins pratique. J'utilise SQL Server 2008.

Merci, Bonne chance, Robert

Était-ce utile?

La solution

Je pense que cela ferait sans sous-requêtes. Avertissement: Ceci est du haut de ma tête, pas testé

.
select distinct computerId
from groupMemberships m1
join groupMemberships m2 on m2.groupId=m1.groupId
  and (m2.isSharing or m2.userId=m1.userId)
join computers c on c.userId=m2.userId
where m1.userId=?

Il n'y a pas besoin de lire le Groupe des tables utilisateur, sauf si il y a d'autres données de ces tables que vous souhaitez inclure dans la sélection que vous ne l'avez pas mentionné.

Le « isSharing ou userId » vous devriez vos propres ordinateurs ainsi que tous les ordinateurs partagés. Cela pourrait être inutilement intelligent:. Une simple union pourrait être plus efficace

Autres conseils

OK, je suppose que vous voulez la table et les requêtes pour la spécification ci-dessus?

J'ai pris des spécifications qu'un ordinateur est « attribué » à un utilisateur donné, mais peut-être partager?

  

Ordinateurs (int userId)

Jetez un oeil à ceci et laissez-moi savoir si vous voulez modifier les spécifications.

DECLARE @Users TABLE(
        UserID INT
)

DECLARE @Computers TABLE(
        ComputerID INT,
        UserID INT
)

DECLARE @Groups TABLE(
        GroupID INT
)

DECLARE @GroupMemberships TABLE(
        UserID INT,
        GroupID INT,
        IsSharing INT
)

INSERT INTO @Users (UserID) SELECT 1
INSERT INTO @Users (UserID) SELECT 2

INSERT INTO @Computers (ComputerID, UserID) SELECT 1, 1
INSERT INTO @Computers (ComputerID, UserID) SELECT 2, 1
INSERT INTO @Computers (ComputerID, UserID) SELECT 3, 1
INSERT INTO @Computers (ComputerID, UserID) SELECT 4, 2
INSERT INTO @Computers (ComputerID, UserID) SELECT 5, 2

INSERT INTO @Groups (GroupID) SELECT 1
INSERT INTO @Groups (GroupID) SELECT 2
INSERT INTO @Groups (GroupID) SELECT 3

INSERT INTO @GroupMemberships (UserID,GroupID,IsSharing) SELECT 1, 1, 0
INSERT INTO @GroupMemberships (UserID,GroupID,IsSharing) SELECT 1, 2, 1
INSERT INTO @GroupMemberships (UserID,GroupID,IsSharing) SELECT 2, 2, 0
INSERT INTO @GroupMemberships (UserID,GroupID,IsSharing) SELECT 2, 3, 0

DECLARE @UserID INT
--SELECT @UserID = 1
SELECT @UserID = 2

SELECT  DISTINCT 
        ComputerID
FROM    @Computers
WHERE   UserID = @UserID
UNION
SELECT  DISTINCT 
        ComputerID
FROM    @Computers c INNER JOIN
        (
            SELECT  DISTINCT 
                    gm.UserID
            FROM    @GroupMemberships gm INNER JOIN
                    @GroupMemberships ThisUserGroups    ON  gm.GroupID = ThisUserGroups.GroupID
                                                        AND ThisUserGroups.UserID = @UserID
            WHERE   gm.UserID != @UserID
            AND             gm.IsSharing = 1
    ) OtherUsersInSharedGroups ON c.UserID = OtherUsersInSharedGroups.UserID
Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top