Sql 如何使用另一个表中的随机行更新表中的每一行
我正在构建我的第一个去识别脚本,我的方法遇到了问题 我有一个表Sql 如何使用另一个表中的随机行更新表中的每一行,sql,sql-server,tsql,random,sql-server-2008-r2,Sql,Sql Server,Tsql,Random,Sql Server 2008 R2,我正在构建我的第一个去识别脚本,我的方法遇到了问题 我有一个表dbo.pseudonames,它的firstname列填充了200行数据。此200行列中的每一行都有一个值(无一行为空)。此表还有一个编号为1-200的id列(int,主键,非null) 我想做的是,在一条语句中,用从我的假名表中随机选择的名字数据重新填充整个用户表 要生成用于拾取的随机数,我使用的是ABS(校验和(NewId())%200。每次我选择ABS(Checksum(NewId())%200时,我都会得到一个数值,该数值在
dbo.pseudonames
,它的firstname
列填充了200行数据。此200行列中的每一行都有一个值(无一行为空)。此表还有一个编号为1-200的id
列(int,主键,非null)
我想做的是,在一条语句中,用从我的假名
表中随机选择的名字
数据重新填充整个用户
表
要生成用于拾取的随机数,我使用的是ABS(校验和(NewId())%200
。每次我选择ABS(Checksum(NewId())%200
时,我都会得到一个数值,该数值在我所寻找的范围内,没有间歇性的不稳定行为
但是,当我在以下语句中使用此公式时:
SELECT pn.firstname
FROM DeIdentificationData.dbo.pseudonyms pn
WHERE pn.id = ABS(Checksum(NewId())) % 200
我得到的结果是断断续续的。我想说,大约30%的结果返回从表中选择的一个名称(这是预期结果),大约30%返回的结果不止一个(令人困惑的是,没有重复的id
列值),大约30%返回的结果为NULL(即使firstname
列中没有空行)
我确实为这个具体问题找了好一阵子,但到目前为止没有结果。我假设这个问题与使用这个公式作为指针有关,但是如果不这样做,我将不知所措
思考?为什么问题中的查询会返回意外结果
SELECT * FROM @VarUsers;
ID UserName
1 PseudonymName41
2 PseudonymName132
3 PseudonymName177
...
998 PseudonymName60
999 PseudonymName141
1000 PseudonymName157
原始查询从笔名中选择。服务器扫描表格的每一行,从该行中选择ID
,生成一个随机数,将生成的数字与ID
进行比较
当为特定行生成的编号碰巧与该行的ID
相同时,该行将在结果集中返回。很有可能生成的编号与ID
不同,并且生成的编号与ID
多次重合
更详细一点:
- 服务器选择ID为1的行
- 生成一个随机数,例如
25
。为什么不呢?一个不错的随机数
- 是否
1=25
?否=>不返回此行
- 服务器选择ID为2的行
- 生成一个随机数,例如
125
。为什么不呢?一个不错的随机数
- 是否
2=125
?否=>不返回此行
- 等等
样本数据
DECLARE @VarPseudonyms TABLE (ID int IDENTITY(1,1), PseudonymName varchar(50) NOT NULL);
DECLARE @VarUsers TABLE (ID int IDENTITY(1,1), UserName varchar(50) NOT NULL);
INSERT INTO @VarUsers (UserName)
SELECT TOP(1000)
'UserName' AS UserName
FROM sys.all_objects
ORDER BY sys.all_objects.object_id;
INSERT INTO @VarPseudonyms (PseudonymName)
SELECT TOP(200)
'PseudonymName'+CAST(ROW_NUMBER() OVER(ORDER BY sys.all_objects.object_id) AS varchar) AS PseudonymName
FROM sys.all_objects
ORDER BY sys.all_objects.object_id;
表Users
有1000行,每行具有相同的UserName
。表假名
有200行不同的假名
:
SELECT * FROM @VarUsers;
ID UserName
-- --------
1 UserName
2 UserName
...
999 UserName
1000 UserName
SELECT * FROM @VarPseudonyms;
ID PseudonymName
-- -------------
1 PseudonymName1
2 PseudonymName2
...
199 PseudonymName199
200 PseudonymName200
第一次尝试
起初我尝试了一种直接的方法。对于用户
中的每一行,我想从假名中随机获得一行
:
SELECT
U.ID
,U.UserName
,CA.PseudonymName
FROM
@VarUsers AS U
CROSS APPLY
(
SELECT TOP(1)
P.PseudonymName
FROM @VarPseudonyms AS P
ORDER BY CRYPT_GEN_RANDOM(4)
) AS CA
;
结果表明,optimizer太聪明了,这产生了一些随机性,但每个用户都有相同的假名
,这不是我所期望的:
ID UserName PseudonymName
1 UserName PseudonymName181
2 UserName PseudonymName181
...
999 UserName PseudonymName181
1000 UserName PseudonymName181
因此,我对这种方法做了一些调整,首先为用户
中的每一行生成一个随机数。然后,我使用生成的编号,使用交叉应用
为用户
中的每一行查找具有该ID的假名
CTE_用户
有一个额外的列,随机数从1到200。在CTE\u Joined
中,我们从假名中为每个用户选择一行。
最后,我们更新原始的Users
表
最终解决方案
WITH
CTE_Users
AS
(
SELECT
U.ID
,U.UserName
,1 + 200 * (CAST(CRYPT_GEN_RANDOM(4) as int) / 4294967295.0 + 0.5) AS rnd
FROM @VarUsers AS U
)
,CTE_Joined
AS
(
SELECT
CTE_Users.ID
,CTE_Users.UserName
,CA.PseudonymName
FROM
CTE_Users
CROSS APPLY
(
SELECT P.PseudonymName
FROM @VarPseudonyms AS P
WHERE P.ID = CAST(CTE_Users.rnd AS int)
) AS CA
)
UPDATE CTE_Joined
SET UserName = PseudonymName;
结果
SELECT * FROM @VarUsers;
ID UserName
1 PseudonymName41
2 PseudonymName132
3 PseudonymName177
...
998 PseudonymName60
999 PseudonymName141
1000 PseudonymName157
使用适当的软件(MySQL、Oracle、DB2等)和版本(例如,sql-server-2014
)标记数据库问题很有帮助。语法和特征的差异通常会影响答案。如果您正在使用SQL Server,您可能需要考虑…更新标签,谢谢<代码>ABS(校验和(NewId())
按行重新计算。你可能想要兰德,但事实并非如此。我喜欢crypt\u gen\u random,很好的发现。但是,我不确定如何将随机数的范围限制在1-200之间?Martin我想得到的是,对于我拉取并更新用户表的每一行,都有一个新的随机值。如果没有对每行重新计算公式,是否意味着我将使用相同的值设置每行?