【问题标题】:Removing "NOT IN" clause and optimising a query删除“NOT IN”子句并优化查询
【发布时间】:2013-04-01 15:27:30
【问题描述】:

我们有一个groupuser permission 表如下:

Group (group_id,name)
01 | G1
02 | G2
03 | G3
04 | G4

UserPermission (userid,group_id,active)
User1 | 01 | 1
User1 | 02 | 1
User2 | 01 | 1
User2 | 02 | 1
User2 | 03 | 1
User2 | 04 | 1
..
UserN | xx | 1

每个用户都拥有一个的权限。
但是 UserPermission 表中缺少某些用户的某些权限。

问题是为UserPermission 表中存在的所有用户插入那些缺少的组权限条目。

所以我想出的查询是 (粗)

 insert into UserPermission 
         select distinct userid, '03', 1 from UserPermission  
         where userid not in (
               select userid from UserPermission where group_id = '03'
         )

这是因为Group 03 的用户缺少插入权限..
其他组类似。

有没有更好的方法来编写上述查询。如何优化?

【问题讨论】:

  • 你在说多少条记录?您是注意到性能问题还是只是一般性地询问?
  • 一般......但大约有大约 30 个组和大约 7K-8K 用户。

标签: sql sql-server tsql query-optimization sql-insert


【解决方案1】:

在我的解决方案中,我从表 GroupUserPermission仅限唯一用户 ID)中获取笛卡尔积。两个表的乘积然后使用LEFT JOIN 连接回表UserPermission,前提是它匹配两列:Group_IDuserID。任何不满足条件的记录都将在UserPermission 的列上具有NULL 值。而这些值就是UserPermission表中的缺失值。

这还将为所有用户填充所有缺失的权限

INSERT INTO UserPermission(userID, group_ID, active)
SELECT  b.userID, a.Group_ID, 1
FROM    [Group] a
        CROSS JOIN (SELECT DISTINCT userID FROM UserPermission) b
        LEFT JOIN UserPermission c
            ON a.Group_ID = c.Group_ID AND
                b.userID = c.UserID
WHERE   c.Group_ID IS NULL

但是如果你只想插入特定的Group_ID,那么你需要额外的条件。

INSERT INTO UserPermission(userID, group_ID, active)
SELECT  b.userID, a.Group_ID, 1
FROM    [Group] a
        CROSS JOIN (SELECT DISTINCT userID FROM UserPermission) b
        LEFT JOIN UserPermission c
            ON a.Group_ID = c.Group_ID AND
                b.userID = c.UserID
WHERE   c.Group_ID IS NULL AND
        a.Group_ID = 3
        -- or change it to IN clause
        -- if you have more Group_ID to include
        -- a.Group_ID IN (3,4)

【讨论】:

  • 通用解决方案很好..不需要硬编码..很好!
  • @Amitd 如果您想硬编码值,我添加了一个更新。 :)
  • 如果我想让它成为特定于用户的查询,它不会改变吗?我猜第四行会有变化吗?
  • 顺便说一句,交叉连接对上述查询有何帮助??
  • 这个查询对我来说非常有效.. 非常感谢 :) 现在被接受为答案
【解决方案2】:

不存在通常比不存在表现更好。

insert into UserPermission
select disinct userid, '03', 1
from UserPermission
where not exists 
(select *
from UserPermission
where group_id = '03')

另一种方法是使用“in”而不是“not in”

insert into UserPermission
select disinct userid, '03', 1
from UserPermission
where user_id in 
(select user_id from UserPermission
except 
select user_id from UserPermission
where group_id = '03')

【讨论】:

    【解决方案3】:

    这是一种方法:

     with toinsert as (
        select userid, '03' as UserPermission
        from UserPermission up
        group by userId
        having sum9case when userPermission = '03' then 1 else 0 end) = 0
     )
     insert into UpserPermission
        select userid, UserPermission
        from toinsert;
    

    我已将插入列表分解为 CTE。您也可以将其作为子查询来执行:

    insert into UserPermission
        select userid, '03'
        from UserPermission
        group by userid
        having sum(case when userpermission = '03' then 1 else 0 end) = 0
    

    【讨论】:

    • 这个查询可以更通用吗?
    • @Amitd 。 . .绝对地。将group byhaving 子句一起用于“set-within-set”问题的优点是所有条件都在having 子句中。这使您可以很容易地概括它。
    【解决方案4】:

    另一个可能的解决方案是使用新的 MERGE 语句。根据我的经验,MERGE 的表现非常好。

    merge UserPermission target
    using (
        -- Assuming u.userid and g.group_id are unique in their tables
        select u.userid, g.group_id
        from [User] u
        cross join [Group] g
    ) source on source.userid = target.userid and source.group_id = target.group_id
    when not matched then
        insert (userid, group_id, active)
        values (source.userid, source.group_id, 1);
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多