我知道游标很糟糕,但我认为我有一个必要的用例。我有一个表(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
答案 0 :(得分:1)
您的功能有问题。它会遇到无限循环。因为在退出内部while循环后你没有将LoopCount
的值重置为0,所以内部while循环只运行一次。这意味着如果你不能在第一次运行中获得唯一的代码,外部while循环将继续无限运行,我认为这是正在发生的事情,因为它无法找到唯一的代码。如果您只想生成随机唯一代码,则可以执行以下操作:SELECT @randomString = CONVERT(varchar(255), NEWID())
。你只需要增加Item_Code的长度。