【问题标题】:SQL Server 2008 - Best way to handle public and private records on the same tableSQL Server 2008 - 处理同一张表上的公共和私人记录的最佳方式
【发布时间】:2014-03-31 20:30:31
【问题描述】:

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

每条记录有 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 秒。

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

谢谢

【问题讨论】:

  • 您在 UserID 上有一个非聚集索引吗?如果您添加一个,它将提高性能。还有其他解决方案,但这是要问的第一个问题。

标签: sql sql-server-2008 private public


【解决方案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;

【讨论】:

  • 你好。在我的完整查询中尝试过,性能是一样的。有没有其他想法让它运行得更快?
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-05-22
  • 1970-01-01
  • 1970-01-01
  • 2010-11-19
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多