截断表不释放LCK_M_SCH_S

时间:2017-05-25 14:50:19

标签: sql-server tsql

我有一个存储过程,它会截断一个不那么大的表(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') 或选择它COUNTobject_id中的执行似乎suspended,我必须关闭查询窗口才能释放锁。我怀疑这是因为我使用交易并通过停止执行将其保持在中间状态,但我不确定。

我知道截断表不会花费太多时间,因为表没有任何外键或索引。

可能是什么问题?我可以使用sys.sysprocesses代替此,但我知道DELETE会更快。

编辑: TRUNCATE而不是DELETE没有任何问题,但我只想将它作为最后的手段使用。

2 个答案:

答案 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