【问题标题】:SQL: UPDATE if not exists, else DELETESQL:如果不存在则更新,否则删除
【发布时间】:2013-01-09 09:10:55
【问题描述】:

我目前正在尝试编写一个脚本,将一个用户的角色更改为另一个,而不在 SQL Server 数据库中创建重复。

例如:

User_ID      Role_ID
---------------------
A            X
A            Z
B            Y
C            X
C            Y
D            Y

用户可能拥有多个角色。

我想更改它,使角色 Y 中的所有用户现在都是角色 X 的成员,并且角色 Y 将不再存在:

User_ID      Role_ID
---------------------
A            X
A            Z
B            X
C            X
D            X

通过将所有 Y 角色更新为 X,这可能会创建重复值;因此我只需要在新值不存在时更新,否则只需删除此值

【问题讨论】:

  • 为什么不直接执行更新并在之后删除重复项?如果这不是一个选项:您使用的是哪个 RDBMS?
  • 这张表有主键吗?
  • 这两列都是外键。
  • 您能否根据上面的示例数据显示预期结果? =)
  • 已更新以显示预期结果。

标签: sql sql-server sql-update not-exists sql-delete


【解决方案1】:

您可以在单个 MERGE 语句中执行此操作(它提供优化的锁定):

MERGE   tbl2
USING   (
                -- Create the desired data (together with duplicates)
        SELECT  User_ID, 
                CASE WHEN Role_ID = 'Y' THEN 'X' ELSE Role_ID END Role_ID
        FROM    tbl2
) new ON tbl2.User_ID = new.User_ID AND tbl2.Role_ID = new.Role_ID
WHEN NOT MATCHED THEN
                -- Insert the updated data
        INSERT(User_ID, Role_ID) VALUES(User_ID, Role_ID)
WHEN NOT MATCHED BY SOURCE THEN
                -- Filter out the 'X'-s
        DELETE;

这是一个SQL Fiddle

【讨论】:

  • MERGE 需要 ; 结尾。
  • 啊,我没有看到嵌入的;)(虽然你不需要一开始的那个)。
  • @Viprus 很高兴为您提供帮助 :)
【解决方案2】:

你首先要清理你的桌子:

DECLARE
  @originalRole char(1) = 'Y',
  @newRole char(1) = 'X'

DELETE [r1] 
FROM [Roles] [r1]
WHERE [r1].[Role_ID] = @originalRole
  AND EXISTS(SELECT *
             FROM [Roles] [r2]
             WHERE [r2].[User_ID] = [r1].[User_ID] AND [r2].[Role_ID] = @newRole)

然后,更新剩下的行,如果有的话:

UPDATE [Roles]
SET [Role_ID] = @newRole
WHERE [Role_ID] = @originalRole

这里是the Fiddle

【讨论】:

  • 预期结果中缺少 A - X。
【解决方案3】:

这可以通过MERGE来完成:

DECLARE @T TABLE (User_ID CHAR(1), Role_ID CHAR(1));
INSERT @T (User_ID, Role_ID)
VALUES ('A', 'X'), ('A', 'Z'), ('B', 'Y'), ('C', 'X'), ('C', 'Y'), ('D', 'Y');

SELECT  *
FROM    @T;

WITH Y AS
(   SELECT  *,
            [d] = COUNT(*) OVER(PARTITION BY User_ID)
    FROM    @T
    WHERE   Role_ID IN ('Y', 'X')
), X AS 
(   SELECT  *
    FROM    @T
    WHERE   Role_ID = 'X'
)
MERGE INTO Y 
USING X ON X.User_ID = Y.User_ID
WHEN MATCHED AND Y.Role_ID = 'Y' AND d > 1 THEN DELETE
WHEN NOT MATCHED BY SOURCE THEN UPDATE
    SET Role_ID = 'X';

SELECT  *
FROM    @T;

【讨论】:

    【解决方案4】:

    试试这个 (SQL DEMO)

    --Delete records with X if same user has Y 
    delete t1
    from userRoles t1 join (
        select * from userRoles t2 where t2.role_id = 'y') t3
        on t1.user_id = t3.user_Id
    where t1.role_id = 'x'
    
    --Update all Y records to X
    update userRoles set role_id = 'X'
    where role_id = 'y'
    
    select * from userRoles
    --RESULTS
    USER_ID ROLE_ID
    A         X
    A         Z
    B         X
    C         X
    D         X
    

    【讨论】:

    • MERGE 语句不处理这个吗?甚至是相关子查询?
    • @ta.speot.is:这是一个一次性的查询,可以完成这项工作。我不确定合并或子查询。你有例子吗?
    • 易于理解并且做得很好。谢谢!
    猜你喜欢
    • 1970-01-01
    • 2014-10-23
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-08-14
    • 1970-01-01
    • 1970-01-01
    • 2022-07-06
    相关资源
    最近更新 更多