0

我有一个关于如何优化查询的问题。实际上,由于我会频繁地运行查询,因此我正在考虑使用物化或索引视图(这是个好主意吗?)还是反规范化。查询/数据库优化:如何优化? (我应该使用物化视图?)

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

  • 用户(INT用户id)
  • 组(INT的groupId)
  • GroupMemberships(INT用户id,整数的groupId,布尔isSharing)
  • 计算机(INT用户id)

的关系是,一个用户可以有0..n的计算机(一个用户到人y计算机)并且可以是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的

感谢, 一切顺利, 罗伯特

回答

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 or userId”应该让你自己的电脑加上任何共享电脑。这可能不必要的巧妙:简单的联合可能更有效。

1

OK,我想你想为上述规格表和查询?

我从计算机是“分配给”一个给定的用户,但可以共享的规格?

计算机(INT用户id)

看一看这一点,让我知道如果你想改变任何规格。

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

啊,谢谢,这看起来像我现在正在用ORM做...但是有两个子类,这个查询是否有效?是否值得将其视为物化视图? – 2009-09-30 10:14:36

+0

是的,每台计算机只有一个用户,但每个用户可能有多台计算机;谢谢! – 2009-09-30 10:17:49

+0

子查询不是必需的,你可以修改它,但这是我输入它的方式,因为我阅读你的问题X-)。 如果表上的索引是好的,我不认为你会有太多的问题。另外,你可能想使用param使用查询或表函数。另外,如果这些值不会定期更改,而且不会缓存这些值,那么您甚至可以为该选择添加一个额外的字段,指示计算机是直接的,还是与其他人共享的。缓存这些值可能会使事情变得更快,但请记住在更新,删除和插入时清除缓存 – 2009-09-30 10:45:27