繁体   English   中英

SQL Server - 在 Update 语句中使用连接

[英]SQL Server - Using joins in Update statement

我有一个HRUser和一个Audit表,两者都在生产中,有大量的行。

现在,我在HRUser表中又添加了一个名为IsActivated

我需要创建一个一次性脚本,该脚本将在生产中执行并将数据填充到此IsActivated列中。 执行此一次性脚本之后,每当用户激活他们的帐户时, HRUser表的IsActivated列将自动更新。

为了更新HRUser表中的IsActivated列,我需要检查Audit表用户是否已经登录到现在。

UPDATE [dbo].HRUser 
SET IsActivated = 1
FROM dbo.[UserAudit] A
JOIN dbo.[HRUser] U ON A.UserId = U.UserId
WHERE A.AuditTypeId = 14

AuditTypeId=14表示用户已登录并且用户可以登录任意次数,并且每次用户登录时都会在UserAudit表中捕获...

逻辑是,如果用户至少登录过一次,则表示该用户已被激活。

这无法在较低的环境中进行测试,需要直接在生产环境中执行,因为在较低的环境中,我们的UserAudit表中没有任何数据。

我不确定这是否有效,因为我从未在更新语句中使用过连接,我正在寻找比这更好的方法来完成我的任务的建议

您可以使用EXISTS和相关子查询来过滤UserId至少有一个 id 14审计事件的行:

UPDATE h
SET IsActivated = 1
FROM [dbo].HRUser h
WHERE EXISTS (
    SELECT 1 FROM 
    FROM dbo.[UserAudit] a
    WHERE a.UserId = h.UserId AND a.AuditTypeId = 14
)

请注意,在子查询中重新打开目标表是没有意义的; 您只需要将它与外部查询相关联。

下面两种方法。 不建议将方法 1 用于“生产中具有大量行”的表。 但是编码要容易得多。 方法 2 可在生产中运行,无需停机。

无论您选择哪种方法:在生产之外对其进行测试。 从生产中复制数据。 如果你不能这样做,那就建立你自己的。 建立一个玩具系统。 强烈建议您在生产中运行任一方法之前,先在某个级别进行测试。

方法 1 :更新连接是直接的。 使用别名。 提醒,这是不推荐“有大量行”和生产运行。 SQL Server 优化器很可能会升级两个表上的锁并阻塞这些表,直到更新完成。 如果您正在停机并且不关心更新需要多长时间,则此方法有效。

UPDATE U 
SET IsActivated = 1
FROM dbo.[UserAudit] A
JOIN dbo.[HRUser] U ON A.UserId = U.UserId
WHERE A.AuditTypeId = 14

方法 2 :如果您无法为此更新停止生产系统(我们大多数人都不能),那么我建议您做两件事:

  1. 使用循环内的事务设置循环。 这意味着优化器将使用行锁而不是阻塞整个表。 这种方法可能需要更长的时间,但不会阻塞生产。 如果更新需要更长的时间,只要 DevOps 团队因为生产被阻止而从不打电话,就不必担心。

  2. 捕获要在事务外更新的行。 然后,基于主键更新(最快)。 总事务时间是更新的行将被阻塞的时间。

这是一个循环的玩具示例。

-- STEP 1:  get data to be updated
CREATE TABLE #selected ( ndx INT IDENTITY(1,1), UserId INT )
INSERT INTO #selected (UserId)
SELECT UserId 
FROM dbo.[UserAudit] A
JOIN dbo.[HRUser] U ON A.UserId = U.UserId
WHERE A.AuditTypeId = 14

-- STEP 2:  update on primary key in steps of 1000
DECLARE @RowsToUpdate INT = 1000
    , @LastId INT = 0
    , @RowCnt INT = 0


DECLARE @v TABLE(ndx INT, UserId INT)

WHILE 1=1
BEGIN 

    DELETE @v 
    
    INSERT INTO @v 
    SELECT TOP(@RowsToUpdate) * 
    FROM #selected WHERE ndx > @LastId
    ORDER BY ndx

    SET @RowCnt = @@ROWCOUNT 
    IF @RowCnt = 0 
    BREAK;

    BEGIN TRANSACTION
        UPDATE a
        SET IsActivated = 1
        FROM @v v
        JOIN dbo.HRUser a ON a.Id = v.UserId 
    COMMIT TRANSACTION

    SELECT @LastId = MAX(ndx) FROM @v 

END 

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM