通过 SQL table 循环生成唯一的随机 ID
Generate unique random IDs looping through SQL table
此问题与此 thread 有关,不确定我是否应该 post 那个 post 中的这个新问题或创建一个新的 post,但是担心它可能不会被视为旧 post ...
中的后续问题
- 我需要用 SQL table 中随机但唯一的新 ID 替换一堆 ID。
- 我正在使用 thread 中 t-clausen.dk 的代码片段来生成新 ID。
- 我正在使用临时 table 遍历旧 ID,生成新 ID,然后用新 ID 更新我的 table。 (本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
虽然你的循环不是我处理问题的方式(基于集合的方法要好得多),但你的问题是你没有在循环内重新初始化 @r
。
所以,添加:
set @r = NULL;
到循环的开头。
我没有对此进行测试,但您可以用此 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 查询将您现有的 externalid 映射到新的 8 字符密钥,然后使用执行更新该映射。
这些字符取自具有 62 个可能字符的文字字符串。值 num 是素数加上一些种子(在本例中为 0)的倍数,并通过对接近可能字符串数的素数取模来保持在限制范围内8 个字符。
然后将生成的数字转换为 8 个字符的字符串,就好像该数字要以 62 进制表示一样。公式中的分母是62的次方
这样可以保证您永远不会重复(除非您的记录多于可能的 8 个字母组合)。
没有随机部分,因此它会始终生成相同的系列,但您可以将 seed 值更改为任何整数以更改将生成的系列。
此问题与此 thread 有关,不确定我是否应该 post 那个 post 中的这个新问题或创建一个新的 post,但是担心它可能不会被视为旧 post ...
中的后续问题- 我需要用 SQL table 中随机但唯一的新 ID 替换一堆 ID。
- 我正在使用 thread 中 t-clausen.dk 的代码片段来生成新 ID。
- 我正在使用临时 table 遍历旧 ID,生成新 ID,然后用新 ID 更新我的 table。 (本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
虽然你的循环不是我处理问题的方式(基于集合的方法要好得多),但你的问题是你没有在循环内重新初始化 @r
。
所以,添加:
set @r = NULL;
到循环的开头。
我没有对此进行测试,但您可以用此 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 查询将您现有的 externalid 映射到新的 8 字符密钥,然后使用执行更新该映射。
这些字符取自具有 62 个可能字符的文字字符串。值 num 是素数加上一些种子(在本例中为 0)的倍数,并通过对接近可能字符串数的素数取模来保持在限制范围内8 个字符。
然后将生成的数字转换为 8 个字符的字符串,就好像该数字要以 62 进制表示一样。公式中的分母是62的次方
这样可以保证您永远不会重复(除非您的记录多于可能的 8 个字母组合)。
没有随机部分,因此它会始终生成相同的系列,但您可以将 seed 值更改为任何整数以更改将生成的系列。