SQL Server 2008 - 处理同一个表上的公共和私有记录的最佳方法

时间:2014-03-31 20:30:31

标签: sql sql-server-2008 private public

我的数据库中有一个记录表,其中有大约一百万条记录。大多数记录都是公开的 - 这意味着系统上的所有用户都可以查看它们。但是在同一张桌子上,我也有私人记录,每个用户通常有几百个。我在系统上有大约1K用户。

每条记录有3个主要栏目:

  • ID - 记录ID的枚举。唯一的主键。
  • UserID - 标识记录所有者。 Null =每个人都可以使用的通用记录。 ID =仅适用于此特定用户ID的专用记录。
  • RecID - 公共记录ID。所有公共记录都是唯一的。如果用户更改了公共记录,系统会使用新ID复制此记录,但RecID

实施例

ID  RecID   UserID  Comments
----------------------------------------------------------------------------
1   1000    NULL    General record
2   1000    1   Modification of record ID=1, available only for userID=1
3   1001    NULL    General Record
4   1002    NULL    General Record
5   1001    2   Modification of record ID=3, available only for userID=2
  • 如果用户1登录系统,他应该获取记录列表2,3,4
  • 如果用户2登录系统,他应该获取记录列表1,4,5
  • 如果用户3登录系统,他应该获取记录列表1,3,4

我使用的查询如下:

SELECT * 
FROM TB_Records
WHERE UserID = @UserID 
   OR (RecID IS NULL AND NOT RecID IN (SELECT RecID 
                                       FROM TB_Records 
                                       WHERE UserID = @UserID)

我遇到的问题是性能问题。在此查询的基础上添加排序过滤和分页结果,每个选择的性能为5-10秒。删除查询的第3行 - 选择所有记录时,性能要好得多,1-2秒。

我想知道是否有更好的方法来处理这样的要求。

由于

1 个答案:

答案 0 :(得分:1)

此查询没有意义。 AND NOT部分是不必要的,因为NULL RecID值不会达到预期效果。我想你的意思是:

SELECT r.* 
FROM TB_Records r
WHERE r.UserID = @UserID OR
      (r.UserId IS NULL AND NOT r.RecID IN (SELECT r2.RecID 
                                            FROM TB_Records r2
                                            WHERE r2.UserID = @UserID)

首先,在TB_Records(UserId, RecId)上创建索引。这可能有所帮助。接下来,我会尝试将其更改为明确的left outer join

select r.*
from TB_Records r left outer join
     TB_Records r2
     on r2.UserId = @UserId and
        r2.RecId = r.RecId
where r.UserId = @UserId or r2.RecId is NULL;

编辑:

使用不同的方法再尝试一次。这使用窗口函数来查看用户是否存在给定记录:

select r.*
from (select r.*,
             max(case when r.UserId = @UserId then 1 else 0 end) over (partition by RecId) as HasUser
      from TB_Records r
     ) t
where r.UserId = @UserId or HasUser = 0;

否则,您应该将执行计划放在问题中。有时,使用union all的查询优于or的查询:

select r.*
from TB_Records r
where r.UserId = @UserId
union all
select r.*
from TB_Records r left outer join
     TB_Records r2
     on r2.UserId = @UserId and
        r2.RecId = r.RecId
where r2.RecId is NULL;