SQL游标循环冻结没有错误

时间:2016-03-22 21:21:10

标签: sql sql-server cursor sql-server-2014

我知道游标很糟糕,但我认为我有一个必要的用例。我有一个表(tbl_Items)与列(Item_Code),我想成为一个独特的,随机生成的字母数字代码(6个字符)。我有一个带有循环的函数,它生成一个随机代码,检查它是否已经存在于表中,并在找到未使用的代码后返回6个字符的代码。

tbl_Items表中的新记录以Item_Code为NULL开始。我需要为所有这些新记录运行UPDATE,并将Item_Code设置为Function的返回值。我显然无法使用单个UPDATE语句执行此操作,因为在整个事务完成之前,函数将无法准确地检查每个新行的Item_Code的唯一性。

因此,CURSOR。这就是我现在所拥有的:

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

    DECLARE @item_id BIGINT
    DECLARE @item_code varchar(5)
    DECLARE @new_code VARCHAR(5)

    DECLARE db_cursor CURSOR
    FORWARD_ONLY 
    FOR
        SELECT Item_ID, Item_Code
        FROM tbl_Items
        WHERE Item_Code IS NULL
    FOR UPDATE OF Item_Code

    OPEN db_cursor
    FETCH NEXT FROM db_cursor INTO @item_id, @item_code

    WHILE (@@FETCH_STATUS = 0)
    BEGIN
        SET @new_code = dbo.generate_unique_code(@item_id)

        UPDATE tbl_Items
        SET Item_Code = @new_code
        WHERE CURRENT OF db_cursor

        print CAST(@item_id AS VARCHAR) + ' - New Code = ' + @new_code + ' (' + CAST(@@FETCH_STATUS AS VARCHAR) + ')'

        FETCH NEXT FROM db_cursor INTO @item_id, @item_code
    END

    CLOSE db_cursor
    DEALLOCATE db_cursor

END

我添加了打印线,只是为了看看发生了什么。我一直在大约10个成功更新的行,然后它完全停止。没有错误或任何东西,它只是保持“正在执行...”但是tbl_Items表不再得到更新,并且在“消息”窗口中没有打印出任何其他内容。

有人能指出我在正确的方向吗? CURSOR的初始查询中有大约40,000条记录。谢谢!

UPDATE:这是返回唯一5个字符代码的函数(5,而不是6)。 “random_view”是一个只有一列的视图,它是0到1之间的随机值。

CREATE FUNCTION [dbo].[generate_unique_code]
(
    @ItemId bigint
)
RETURNS varchar(5)
AS
BEGIN
    DECLARE @Length int
    DECLARE @CharPool varchar(36)
    DECLARE @PoolLength int
    DECLARE @LoopCount int
    DECLARE @RandomString varchar(5)
    DECLARE @RandomInt decimal(18,18)
    DECLARE @IsUnique bit
    DECLARE @CodeExists bigint = NULL

    SET @Length = 6
    SET @CharPool = 'abcdefghijklmnopqrstuvwxyz0123456789'
    SET @PoolLength = Len(@CharPool)

    SET @LoopCount = 0
    SET @RandomString = ''
    SET @IsUnique = 0

    WHILE (@IsUnique = 0) BEGIN
        -- GENERATE UNIQUE 5 CHARACTER STRING
        WHILE (@LoopCount < @Length) BEGIN
            SELECT @RandomInt = rnd
            FROM random_view
            SELECT @RandomString = @RandomString + SUBSTRING(@Charpool, CONVERT(int, @RandomInt * @PoolLength), 1)
            SELECT @LoopCount = @LoopCount + 1
        END
        -- CHECK IF CODE ALREADY EXISTS IN THE DATABASE
        SELECT @CodeExists = Item_ID
        FROM tbl_Items
        WHERE Item_Code = LEFT(@RandomString,5)

        -- IF CODE IS NOT ALREADY IN THE DATABASE, BREAK LOOP TO USE IT
        IF @CodeExists IS NULL BEGIN
            SET @IsUnique = 1
        END
    END

    RETURN LEFT(@RandomString,5)
END

1 个答案:

答案 0 :(得分:1)

您的功能有问题。它会遇到无限循环。因为在退出内部while循环后你没有将LoopCount的值重置为0,所以内部while循环只运行一次。这意味着如果你不能在第一次运行中获得唯一的代码,外部while循环将继续无限运行,我认为这是正在发生的事情,因为它无法找到唯一的代码。如果您只想生成随机唯一代码,则可以执行以下操作:SELECT @randomString = CONVERT(varchar(255), NEWID())。你只需要增加Item_Code的长度。