如何在存储过程中优化游标

时间:2018-06-25 16:58:21

标签: sql-server

我在一个表上进行迭代的存储过程遇到了问题,它可以在几百行上正常工作,但是当表超过数千行时,它会使内存饱和并崩溃。

该过程应逐行迭代,并用从该行中另一列计算出的值填充一列。我怀疑是游标使程序崩溃,在其他问题上,我已经读过使用while循环,但我不是sql专家,因此我尝试从这些答案中尝试的示例无效。

CREATE PROCEDURE [dbo].[GenerateNewHashes]
AS
BEGIN
    SET NOCOUNT ON;

    DECLARE @module BIGINT = 382449983

    IF EXISTS(SELECT 1 FROM dbo.telephoneSource WHERE Hash IS NULL)
    BEGIN
        DECLARE hash_cursor CURSOR FOR 
             SELECT a.telephone, a.Hash 
             FROM dbo.telephoneSource AS a

        OPEN hash_cursor 

        FETCH FROM hash_cursor 

        WHILE @@FETCH_STATUS = 0
        BEGIN
            UPDATE dbo.telephoneSource 
            SET Hash = CAST(telephone AS BIGINT) % @module 
            WHERE CURRENT OF hash_cursor

            FETCH NEXT FROM hash_cursor
        END

        CLOSE hash_cursor 
        DEALLOCATE hash_cursor 
    END
END

基本上,该存储过程旨在填充添加到现有表中的称为Hash的新列,当更新表的脚本结束时,新列将填充为NULL值,然后该存储过程应该填充每个空值,带有操作电话号码(这是一个bigint)%模块变量(也包括big int)。

除了更改为while循环外,还有什么可以使它使用更少的内存或只是不崩溃的方法?预先感谢。

3 个答案:

答案 0 :(得分:2)

您可以执行以下操作:

WHILE 1=1
BEGIN
    UPDATE TOP (10000) dbo.telephoneSource
    SET Hash = CAST(telephone AS BIGINT)%@module
    WHERE Hash IS NULL;

    IF @@ROWCOUNT = 0
    BEGIN
        BREAK;
    END;
END;

只要有Hash个值,这就会更新NULL,并且在没有记录更新时将退出。

添加过滤索引也可能很有用:

CREATE NONCLUSTERED INDEX IX_telephoneSource_Hash_telephone
    ON dbo.telephoneSource (Hash)
    INCLUDE (telephone)
WHERE Hash IS NULL;

它将加快查找速度以进行更新。但这可能不是必需的。

答案 1 :(得分:0)

这是在上面的注释中不使用游标就在循环中执行此操作的代码示例,并且如果将要更新的字段添加为IS NOT NULL到内部循环中,它将不会更新已完成的操作(在情况下,您需要重新启动该过程。

我没有在其中包括您的特定表,但是如果您需要我可以在其中添加它。

DECLARE @PerBatchCount as int
DECLARE @MAXID as bigint
DECLARE @WorkingOnID as bigint

Set @PerBatchCount = 1000

--Find range of IDs to process using yoru tablename
SELECT @WorkingOnID = MIN(ID), @MAXID = MAX(ID)
FROM YouTableHere WITH (NOLOCK)


WHILE @WorkingOnID <= @MAXID
    BEGIN 
        -- do an update on all the ones that exist in the offer table NOW
        --DO YOUR UPDATE HERE
        -- include this where clause where ID is your PK you are looping through    
        WHERE ID BETWEEN @WorkingOnID AND (@WorkingOnID + @PerBatchCount -1)

    set @WorkingOnID = @WorkingOnID + @PerBatchCount  

END

SET NOCOUNT OFF;

答案 2 :(得分:0)

我只需添加计算列:

ALTER TABLE dbo.telephoneSource
ADD Hash AS (CAST(telephone AS BIGINT)%382449983) PERSISTED;
相关问题