我有一个存储过程,它会截断一个不那么大的表(2M记录,但将来会变大),然后重新填充它。示例版本如下:
ALTER PROCEDURE [SC].[A_SP]
AS
BEGIN
BEGIN TRANSACTION;
BEGIN TRY
TRUNCATE TABLE SC.A_TABLE
IF OBJECT_ID('tempdb..#Trans') IS NOT NULL DROP TABLE #Trans
SELECT
*
INTO
#Trans
FROM
(
SELECT
...
FROM
B_TABLE trans (NOLOCK)
INNER JOIN
... (NOLOCK) ON ...
LEFT OUTER JOIN
... (NOLOCK) ON ...
...
) AS x
INSERT INTO
SC.A_TABLE
(
...
)
SELECT
...
FROM
#Trans (NOLOCK)
DROP TABLE #Trans
END TRY
BEGIN CATCH
IF @@TRANCOUNT > 0
ROLLBACK TRANSACTION;
THROW
END CATCH
IF @@TRANCOUNT > 0
COMMIT TRANSACTION;
END
此过程需要几个小时才能完成。有时我想通过使用COUNT
来查看完成了多少:
SELECT COUNT(*) FROM A_TABLE (NOLOCK)
这不会返回任何内容(即使使用NOLOCK
),因为LCK_M_SCH_S
语句导致表{4}}锁定。我甚至做不到:
TRUNCATE
另一个有趣的事情是;我有时会通过SSMS停止执行该程序,甚至在此之后我无法选择SELECT object_id('SC.A_TABLE')
或选择它COUNT
。 object_id
中的执行似乎suspended
,我必须关闭查询窗口才能释放锁。我怀疑这是因为我使用交易并通过停止执行将其保持在中间状态,但我不确定。
我知道截断表不会花费太多时间,因为表没有任何外键或索引。
可能是什么问题?我可以使用sys.sysprocesses
代替此,但我知道DELETE
会更快。
编辑: TRUNCATE
而不是DELETE
没有任何问题,但我只想将它作为最后的手段使用。
答案 0 :(得分:1)
如果Truncate不是你的行李并且你有太多行可以执行删除而不会使TLog陷入崩溃停滞状态,那么总会有选项 UbW (对于丑陋但可行):创建表的克隆,将行加载到表中,然后(并在事务内)切换所有内容。
选项 UbW2 建立在这个概念的基础上 - 总是建立两个表 - 一个空,一个完整。加载到空表中,然后修改同义词视图以指向该表。
选项 LUbW (不那么难看...)涉及使用分区:将数据加载到交换机表中,然后使用某个标志作为分区函数将其作为分区移动。
所有这些都需要更多的工作和代码。我们有类似的情况,并为我们的数据仓库使用选项 UbW2 ,它允许我们将数百万行加载到“活动”表中,每小时没有停机时间,也不会让消费者看到不一致的数据。
答案 1 :(得分:0)
最好的办法是将一些繁重的工作转移到交易中。也就是说,Sql Server按设计工作100%。
ALTER PROCEDURE [SC].[A_SP]
AS
BEGIN
IF OBJECT_ID('tempdb..#Trans') IS NOT NULL DROP TABLE #Trans
SELECT
*
INTO
#Trans
FROM
(
SELECT
...
FROM
B_TABLE trans (NOLOCK)
INNER JOIN
... (NOLOCK) ON ...
LEFT OUTER JOIN
... (NOLOCK) ON ...
...
) AS x
BEGIN TRANSACTION;
BEGIN TRY
TRUNCATE TABLE SC.A_TABLE
INSERT INTO
SC.A_TABLE
(
...
)
SELECT
...
FROM
#Trans (NOLOCK)
DROP TABLE #Trans
If @@TranCount >0 And Xact_State() = 1
Commit Transaction;
END TRY
BEGIN CATCH
IF @@TRANCOUNT > 0
ROLLBACK TRANSACTION;
THROW
END CATCH