0

J'ai une question sur la façon d'optimiser une requête. En fait, comme je vais lancer la requête fréquemment, je pensais utiliser une vue matérialisée ou indexée (est-ce une bonne idée ici?) Ou dénormaliser.Optimisation de requête/base de données: comment optimiser ceci? (et devrais-je utiliser une vue matérialisée?)

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

  • utilisateurs (int userId)
  • Groupes (int groupId)
  • GroupMemberships (int userId, int groupId, bool isSharing)
  • ordinateurs (int userid)

Les relations sont qu'un utilisateur peut avoir 0..n ordinateurs (un utilisateur à l'homme y ordinateurs) et peut être membre de 0..n groupes. Un groupe peut avoir 0..n utilisateurs (de nombreux utilisateurs à plusieurs groupes). Le "isSharing" indique si un utilisateur partage avec ce groupe ou est un membre "en lecture seule" de ce groupe (c'est-à-dire, peut voir le partage des ordinateurs des membres, mais ne partage pas le sien).

La requête consiste à trouver, pour un utilisateur donné, les ordinateurs que cet utilisateur peut voir. Un utilisateur peut voir tous ses propres ordinateurs. Elle peut également voir tous les ordinateurs d'autres utilisateurs appartenant à des groupes dont elle est membre et qui 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 bon sur l'ensemble de la chose SQL), mais c'est évidemment une solution moins qu'idéale. J'ai des index sur tous les champs que j'ai listés ici (sauf isShared) et un index supplémentaire sur le tuple GroupMembership (userId, groupId). Mais les assistants de base de données peuvent-ils penser à une meilleure solution? Le projet n'est pas encore en ligne, mais je suppose qu'il y aurait en moyenne 1,2 ordinateur par utilisateur (tout le monde en aura un, quelques-uns en auront peut-être plus) et peut-être 0,75 appartenance à un groupe par utilisateur t utiliser la fonction de groupe, mais ceux qui le feront seront probablement membres de plusieurs groupes). En outre, toutes ces tables associées recevront des ajouts fréquents, ce qui peut rendre les vues matérialisées une solution moins pratique. J'utilise SQL Server 2008.

Merci, Bonne chance, Robert

Répondre

1

Je pense que cela le ferait sans aucune sous-requête. Disclaimer: C'est hors 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 or userId" devrait vous procurer vos propres ordinateurs ainsi que tous les ordinateurs partagés. Cela pourrait être inutilement intelligent: une union simple pourrait être plus efficace.

1

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 "assigné" à un utilisateur donné, mais peut être partagé?

Ordinateurs (int userid)

Jetez un oeil à cela 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 
+0

Ah, merci, ça ressemble à ce que je fais en ce moment avec ORM ... mais avec deux sous-requêtes, cette requête sera-t-elle efficace? Et vaut-il la peine d'en faire une vision matérialisée? –

+0

Oui, il n'y a qu'un seul utilisateur par ordinateur, mais peut-être plusieurs ordinateurs par utilisateur; Merci! –

+0

La sous-requête n'est pas nécessaire, vous pouvez le modifier, mais c'est comme ça que je l'ai tapé en lisant votre question X-). Si l'indexation sur les tables est bonne, je ne pense pas que vous aurez trop de problèmes. En outre, vous voudrez probablement utiliser une fonction de requête ou de table en utilisant le param. De même, si les valeurs ne changent pas régulièrement, sans mettre en cache les valeurs, vous pouvez même ajouter un champ supplémentaire à la sélection, indiquant si l'ordinateur est direct ou partagé par un autre. La mise en cache des valeurs peut rendre les choses beaucoup plus rapides, mais n'oubliez pas d'effacer le cache lors des mises à jour, des suppressions et des insertions –

Questions connexes