删除“NOT IN”子句并优化查询
Posted
技术标签:
【中文标题】删除“NOT IN”子句并优化查询【英文标题】:Removing "NOT IN" clause and optimising a query 【发布时间】:2013-04-01 15:27:30 【问题描述】:我们有一个group
和user 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 用户。 【参考方案1】:在我的解决方案中,我从表 Group
和 UserPermission
(仅限唯一用户 ID)中获取笛卡尔积。两个表的乘积然后使用LEFT JOIN
连接回表UserPermission
,前提是它匹配两列:Group_ID
和userID
。任何不满足条件的记录都将在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
SQLFiddle Demo
但是如果你只想插入特定的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)
SQLFiddle Demo
【讨论】:
通用解决方案很好..不需要硬编码..很好! @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 by
与having
子句一起用于“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);
【讨论】:
以上是关于删除“NOT IN”子句并优化查询的主要内容,如果未能解决你的问题,请参考以下文章