0

我有一个关于如何优化查询的问题。实际上,由于我要经常运行查询,我正在考虑使用物化视图或索引视图(这是一个好主意吗?)或非规范化。

考虑以下四个表(省略了不相关的字段):

  • 用户(int userId)
  • 组(int groupId)
  • GroupMemberships (int userId, int groupId, bool isSharing)
  • 计算机(int userId)

关系是一个用户可以拥有 0..n 台计算机(一个用户对多台计算机)并且可以是 0..n 个组的成员。一个组可以有 0..n 个用户(许多用户对许多组)。“isSharing”表示用户是否正在与该组共享,或者是该组的“只读”成员(即,可以看到共享成员的计算机,但不能共享她自己的)。

查询是为给定用户查找该用户可以看到哪些计算机。用户可以看到她自己的所有计算机。她还可以查看属于她的成员并与该组共享的组中的其他用户的任何计算机。好的,这没有多大意义,所以这是 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

现在我正在使用 ORM 映射器并基本上执行上述操作(我对整个 SQL 事情不太擅长),但这显然是一个不太理想的解决方案。我在此处列出的每个字段(isShared 除外)上都有索引,在 GroupMembership 的 (userId, groupId) 元组上有一个额外的索引。但是那里的任何数据库向导都可以想到更好的解决方案吗?

该项目尚未上线,但我猜每个用户平均可能有 1.2 台计算机(每个人都有一台,少数人可能有更多),每个用户可能有 0.75 个组成员资格(许多用户不会使用这些组功能,但这样做的人可能是多个组的成员)。此外,所有这些关联的表都将频繁添加,这可能会使物化视图成为不太实用的解决方案。我正在使用 SQL Server 2008。

谢谢,一切顺利,罗伯特

4

2 回答 2

1

好的,我认为您想要上述规范的表格和查询?

我从规范中得知计算机已“分配”给给定用户,但可以共享吗?

计算机(int userId)

看看这个,如果你想改变任何规格,请告诉我。

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
于 2009-09-30T08:26:32.833 回答
1

我认为这将在没有任何子查询的情况下完成。免责声明:这是我的想法,未经测试。

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=?

无需读取用户组表,除非您希望将这些表中的其他数据包含在您未提及的选择中。

“isSharing 或 userId”应该让您拥有自己的计算机以及任何共享计算机。这可能是不必要的聪明:一个简单的联合可能更有效。

于 2009-10-01T21:05:11.797 回答