【问题标题】:insert foreach x where not exists在不存在的地方插入 foreach x
【发布时间】:2012-07-12 18:12:12
【问题描述】:

我有一个相当简单的数据库,由三个(相关)表组成:用户、权限和用户权限。基本前提很简单:创建用户时,权限表中的所有记录都会自动添加到 user_permissions 表中,并具有默认值。这一切都很好。

但是,由于我目前处于开发阶段,我会继续添加新权限,现有用户当然不会拥有这些权限,因为这些新权限在创建时不存在于权限表中。所以,我有一个绝妙的主意,创建了一个小存储过程来自动更新 user_permissions 表,其中包含 user_permissions 表中当前不存在的所有权限。

所以,简而言之,我想做的是(伪代码)

For each user without x permission in user_permissions, insert into user_permissions user_id and permission_id

我不太确定如何从 SQL POV 执行此操作。我玩过连接并且“不存在”,但还没有真正到达任何地方。

你可以在这里玩我的架构:http://sqlfiddle.com/#!3/b0761/3 提前感谢您的帮助!

编辑:架构:

CREATE TABLE users (
  user_id      int IDENTITY(1, 1) NOT NULL,
  user_name    varchar(255),
  PRIMARY KEY (user_id));

CREATE TABLE permissions (
  permission_id          int IDENTITY(1, 1) NOT NULL, 
  permission_name        varchar(255) NOT NULL, 
  PRIMARY KEY (permission_id));

CREATE TABLE user_permissions (
  user_id       int NOT NULL, 
  permission_id int NOT NULL, 
  value         tinyint DEFAULT 0 NOT NULL,  
  PRIMARY KEY (user_id, 
  permission_id));

ALTER TABLE user_permissions ADD CONSTRAINT FK_user_pe338140 
FOREIGN KEY (permission_id)
REFERENCES permissions (permission_id);

ALTER TABLE user_permissions ADD CONSTRAINT FK_user_pe405324 
FOREIGN KEY (user_id) REFERENCES users (user_id);

INSERT INTO users(user_name) values('test_username');
INSERT INTO users(user_name) values('test_username2');

INSERT INTO permissions(permission_name) VALUES('permission_1')
INSERT INTO permissions(permission_name) VALUES('permission_2')

INSERT INTO user_permissions(user_id, permission_id, value)
VALUES(1, 1, 1)

INSERT INTO user_permissions(user_id, permission_id, value)
VALUES(2, 1, 1)

编辑:查询到目前为止

SELECT a.user_id, b.permission_id, 1 as 'value'
FROM USER_PERMISSIONS a right outer join 
PERMISSIONS b on a.permission_id = b.permission_id

【问题讨论】:

标签: sql sql-server sql-server-2005 stored-procedures


【解决方案1】:
insert into user_permissions (user_id, permission_id)
select
  u.user_id,
  p.permission_id
from
  users u
  cross join permissions p
where
  not exists (select 0 from user_permissions with (updlock, holdlock)
              where user_id = u.user_id and permission_id = p.permission_id)

参考:Only inserting a row if it's not already there

【讨论】:

    【解决方案2】:
    INSERT dbo.user_permissions([user_id], permission_id, [value])
    SELECT u.[user_id], p.permission_id, 1
    FROM dbo.user_permissions AS u 
    CROSS JOIN dbo.[permissions] AS p
    WHERE NOT EXISTS (SELECT 1 FROM dbo.user_permissions 
      WHERE [user_id] = u.[user_id]
      AND permission_id = p.permission_id
    )
    GROUP BY [user_id], p.permission_id;
    

    顺便说一句,您应该避免使用往往需要分隔符的名称,例如user_idpermissions 是关键字/保留词。

    【讨论】:

    • 我曾经使用这种模式,直到我意识到我可以这样表达:FROM TableA A INNER JOIN TableC C ON EXISTS (SELECT * FROM TableB B WHERE A.AID = B.AID AND B.CID = C.CID)。我更喜欢它,因为它避免了作为占位符的 CROSS JOIN 来获取表名,这样您就可以 EXISTS 了,而且它会将 EXISTS 从 WHERE 子句中移出,ANSI 样式。
    【解决方案3】:

    曼斯菲尔德, 如果我理解正确,您希望在添加新权限时为 user_permissions 表添加一个值。

    我还假设您希望它默认为 0。插入新权限后,运行此代码将为所有用户播种 user_permissions 表,对于当前未使用的任何权限,默认值为 0。

    --insert into permissions(permission_name) values('newperm');
    --select * from permissions
    insert into user_permissions(user_id, permission_id, value)
    select
      u.user_id, p.permission_id, 0
    from
                 users u
      cross join permissions p
    where
          p.permission_id not in(select permission_id from user_permissions)
    ;
    --select * from user_permissions;
    

    【讨论】:

    • 如果只为一个用户添加了新权限怎么办? (根据问题中的示例代码,默认值为 1。)
    • “所以,我有一个绝妙的主意,创建了一个小存储过程,以使用 user_permissions 表中当前不存在的所有权限自动更新 user_permissions 表。”所以,他说一个假设是它没有被使用。 :)
    • 当然可以,但是为什么不编写查询以使您免受事情没有完全按预期执行的情况呢?我管理的系统完全像这样,当然我们让开发人员只为他们的用户测试东西。
    【解决方案4】:

    以下查询将为您提供要插入的缺少的用户权限行:

    select a.USER_ID,b.permission_id from users a,permissions b,user_permissions c
       where c.user_id <>a.user_id and c.permission_id <> b.permission_id
    

    【讨论】:

    • 呃,老式笛卡尔连接,真的吗?
    • 我讨厌这样做,但这是实现作者期望的方法之一。
    • @Esen 这应该证明:sqlfiddle.com/#!3/39a3b/1(我添加了更多数据)
    • 试试sqlfiddle.com/#!3/eda3c/1 - 如果我向表中添加数据,您的查询应该返回更少行,而不是更多 行。
    • 如果你能成功,我也很高兴再次投票 :) 总是乐于帮助那些努力帮助我的人!
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-09-16
    • 2016-03-24
    • 2018-10-12
    • 2019-02-17
    • 1970-01-01
    相关资源
    最近更新 更多