SQL 游标循环冻结没有错误
SQL Cursor loop freezing with no error
我知道游标不好,但我认为我有一个需要游标的用例。我有一个 table (tbl_Items),其中有一列 (Item_Code),我希望它是一个唯一的、随机生成的字母数字代码(6 个字符)。我有一个带有循环的函数,它生成一个随机代码,检查它是否已经存在于 table 中,一旦找到一个未使用的代码,return 就是 6 字符代码。
tbl_Items table 中的新记录从 Item_Code 开始为 NULL。我需要 运行 更新所有这些新记录,并将 Item_Code 设置为函数的 return 值。我显然不能用一个 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 个成功更新的行,然后它就完全停止了。没有错误或任何错误,它只是保持 "Executing..." 但 tbl_Items table 不再更新,消息 window.[=13= 中没有其他内容打印出来]
谁能给我指出正确的方向? CURSOR 的初始查询中大约有 40,000 条记录。谢谢!
更新: 这是 return 唯一的 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
你的功能有问题。它会运行进入死循环。因为您在退出内部 while 循环后没有将 LoopCount
的值重置为 0,所以内部 while 循环只会 运行 一次。这意味着如果您无法在第一个 运行 中获得唯一代码,则外部 while 循环将无限地保持 运行ning 我认为这就是正在发生的事情,因为它找不到唯一代码代码。如果你只想生成随机的唯一代码,你可以这样做:SELECT @randomString = CONVERT(varchar(255), NEWID())
。您只需要增加 Item_Code.
的长度
我知道游标不好,但我认为我有一个需要游标的用例。我有一个 table (tbl_Items),其中有一列 (Item_Code),我希望它是一个唯一的、随机生成的字母数字代码(6 个字符)。我有一个带有循环的函数,它生成一个随机代码,检查它是否已经存在于 table 中,一旦找到一个未使用的代码,return 就是 6 字符代码。
tbl_Items table 中的新记录从 Item_Code 开始为 NULL。我需要 运行 更新所有这些新记录,并将 Item_Code 设置为函数的 return 值。我显然不能用一个 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 个成功更新的行,然后它就完全停止了。没有错误或任何错误,它只是保持 "Executing..." 但 tbl_Items table 不再更新,消息 window.[=13= 中没有其他内容打印出来]
谁能给我指出正确的方向? CURSOR 的初始查询中大约有 40,000 条记录。谢谢!
更新: 这是 return 唯一的 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
你的功能有问题。它会运行进入死循环。因为您在退出内部 while 循环后没有将 LoopCount
的值重置为 0,所以内部 while 循环只会 运行 一次。这意味着如果您无法在第一个 运行 中获得唯一代码,则外部 while 循环将无限地保持 运行ning 我认为这就是正在发生的事情,因为它找不到唯一代码代码。如果你只想生成随机的唯一代码,你可以这样做:SELECT @randomString = CONVERT(varchar(255), NEWID())
。您只需要增加 Item_Code.