生成循环遍历SQL表的唯一随机ID

时间:2016-03-12 17:11:21

标签: sql-server tsql loops random uniqueidentifier

此问题与此thread有关,不确定我是否应该在该帖子中发布此新问题或创建新帖子,但担心它可能不会被视为内容中的后续问题老帖子......

  • 我需要在SQL表中用随机但唯一的新ID替换一堆ID。
  • 我正在使用来自此thread的t-clausen.dk代码段来生成新ID。
  • 我正在使用临时表来迭代旧ID,生成新ID,然后使用新ID更新我的表。 (此thread中的解决方案的那部分)
  • 我的问题是所有新ID最终都是相同的。如何让@r清除以便生成新号码?
  • 或者是否有更好的方法来解决这个问题?说没有循环...?

    SELECT * INTO #ControlTable FROM tempmaster 
    DECLARE @ei varchar(max)
    DECLARE @r  varchar(8)
    
    WHILE EXISTS (SELECT * FROM #ControlTable)
    BEGIN
        SELECT @ei = (SELECT TOP 1 externalid FROM #ControlTable ORDER BY externalid ASC)
    
        -- CREATE UNIQUE RANDOM ID
        SELECT @r = coalesce(@r, '') + n
        FROM (SELECT top 8 
        CHAR(number) n FROM
        master..spt_values
        WHERE type = 'P' AND 
        (number between ascii(0) and ascii(9)
        or number between ascii('A') and ascii('Z')
        or number between ascii('a') and ascii('z'))
        ORDER BY newid()) a
    
        -- REPLACE OLD ID
        UPDATE tempmaster SET externalid  = @r WHERE externalid = @ei
    
        DELETE #ControlTable WHERE externalid = @ei
    
        /*TESTING*/
        --SELECT @ei AS EI, @r AS [newID]
        --SELECT * FROM #ControlTable
        --SELECT * FROM tempmaster WHERE externalid = @ei OR externalid = @r
    END
    
    drop table #ControlTable
    

这是基于集合的方法的尝试概述

    DECLARE @r varchar(8);
    SELECT oid, startdate, enddate,
        coalesce(@r, '') + n
        FROM (SELECT TOP 8 
        CHAR(number) n FROM
        master..spt_values
        WHERE type = 'P' AND 
        (number between ascii(0) and ascii(9)
        or number between ascii('A') and ascii('Z')
        or number between ascii('a') and ascii('z'))
        ORDER BY newid()) 
    as externalid
    FROM MasterTable

2 个答案:

答案 0 :(得分:2)

虽然你的循环不是我接近问题的方法(基于集合的方法要好得多),但问题是你没有在循环内重新初始化@r

所以,添加:

set @r = NULL;

到循环的开头。

答案 1 :(得分:1)

我没有对此进行测试,但您可以通过此UPDATE语句替换循环。它要求您当前的 enternalid 值是唯一的:

WITH cte AS (
    SELECT
        externalid,
        concat(
            substring(chars, num % 62, 1),
            substring(chars, (num/62) % 62, 1),
            substring(chars, (num/3844) % 62, 1),
            substring(chars, (num/238328) % 62, 1),
            substring(chars, (num/14776336) % 62, 1),
            substring(chars, (num/916132832) % 62, 1),
            substring(chars, (num/56800235584) % 62, 1),
            substring(chars, (num/3521614606208) % 62, 1)
        ) AS unique_string8 
    FROM (
        SELECT
            externalid,
            (rn * 34524689549219 + seed) % 199689672115897 AS num,
            '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz' AS chars
        FROM (
            SELECT externalid,
                   ROW_NUMBER() OVER (ORDER BY externalid) AS rn,
                   0 AS seed
            FROM   tempmaster
        ) AS subq1
    ) AS subq2
)
UPDATE     t
SET        externalid = cte.unique_string8
FROM       tempmaster t
INNER JOIN cte
        ON cte.externalid = t.externalid;

我们的想法是 cte 查询使用新的8个字符的密钥映射现有的 externalid ,然后使用该映射执行更新。

字符取自具有62个可能字符的文字字符串。值 num 是素数加上一些种子(在这种情况下为0)的倍数,并通过将模数取为接近8个字符的可能字符串数的素数而保持在限制范围内。

然后将得到的数字转换为8个字符的字符串,就好像该数字用62个碱基表示一样。公式中的分母是62的幂。

这样,保证您永远不会得到重复(除非您有比8个字母组合更多的记录)。

没有随机部分,因此它将始终生成相同的系列,但您可以将种子值更改为任何整数以更改将生成的系列。