如何最好地删除 table 并通过重命名另一个 table 来替换它?

How best to drop a table and replace it by renaming another table?

我正在使用 SQL Server 2019,我有一个简单的存储过程可以刷新我的报告数据库中的大型 table,整个过程中各种报告和数据可视化经常使用它公司:

 TRUNCATE TABLE Reporting.dbo.TempA
    INSERT INTO Reporting.dbo.TempA WITH(TABLOCKX)
         SELECT RowID,
                ISNULL(CustomerData,''),
           FROM [PROD].LiveData.dbo.A WITH(NOLOCK)
          WHERE RowID IS NOT NULL

运行 只需几分钟,但其他用户或报告可能会在 INSERT INTO 运行ning 时尝试访问此 table。对他们来说,它会显示为空或锁定,具体取决于我为 INSERT INTO 执行的锁定类型,这两种结果都不是真正的 acceptable.

我想做的不是 t运行cating TempA,而是创建一个新的 table 命名为 TempB,填充它,然后删除 TempA 并将 TempB 重命名为 TempA。

但是,我认为仍然有可能在我删除它和重命名 TempB 之间的瞬间执行对 TempA 的其他请求。在这种情况下,该错误将比锁定或空 table 更严重,它将是“Table 不存在”错误。

我错了吗?那永远不可能吗?我是否应该在同一事务中关闭并执行 drop 命令和重命名命令,让 SQL 服务器执行它的操作?我是否应该将这两个命令都包装在 BEGIN TRAN 中以更加安全?

或者如果我是对的,有什么办法可以防止这种行为吗?我是否可以将 TempA 置于“锁定”状态,即使我刚刚将其删除并且我正准备将另一个 table 重命名为 TempA?当“锁定”状态过期时,任何其他进程或查询是否可以正常访问 TempA,就好像它从未被删除过一样?

感谢任何帮助。

I think there's still a chance that some other request on to TempA might be executed in the split second between me dropping it and renaming TempB

是的,除非你阻止它,否则它会发生。这甚至不是不可能的。第一个 sp_rename 将等待并获取 T 上的独占模式锁。当它持有该锁时,任何其他想要查询 T 的会话都将被阻止。所以当重命名提交时,很可能有会话一直在等待,并会立即尝试查询它。

为防止出现这种情况,您需要持有锁,直到用新 table 替换 T。为此,只需使用一个事务:

begin transaction
exec sp_rename 'T','T_old'
exec sp_rename 'T_stage','T'
commit transaction

第一个 sp_rename 将在 table 上获得独占 table 锁 (Sch-M),其他会话将被阻塞,直到 COMMIT TRANSACTION。

您的第一种方法,即 Truncate/Load 比创建新的 table、重命名并删除旧的更简单且更好。将您的 ETL truncate/load 安排在非工作时间,以尽量减少影响。 另一种方法是使用 table 分区,将数据加载到新分区并删除旧分区,这样在任何给定时间点你的 table 永远不会为空。