我正在构建我的第一个去识别脚本,并遇到了我的方法问题。
我有一个表dbo.pseudonyms
,其firstname
列填充了200行数据。此列中包含200行的每一行都有一个值(none为null)。该表还有一个id
列(int,主键,非null),数字为1-200。
我想要做的是,在一个声明中,重新填充我的整个USERS
表,并为我firstname
表中每行随机选择pseudonyms
个数据。
使用ABS(Checksum(NewId())) % 200
生成随机数以便选择我。每次我做SELECT 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
列中有没有空行)
我确实为这个具体问题寻找了一段时间,但到目前为止无济于事。我假设问题与使用此公式作为指针有关,但我不知道如何做到这一点。
思想?
答案 0 :(得分:1)
为什么问题中的查询会返回意外结果
您的原始查询从Pseudonyms
中选择。服务器扫描表格的每一行,从该行中选择ID
,生成一个随机数,将生成的数字与ID
进行比较。
如果偶然生成的特定行的编号恰好与该行的ID
相同,则在结果集中返回此行。偶然生成的数字很可能永远不会与ID
相同,并且生成的数字会多次与ID
重合。
更详细一点:
ID=1
行。25
。为什么不?一个不错的随机数。1 = 25
?否=>不返回此行。ID=2
行。125
。为什么不?一个不错的随机数。2 = 125
?否=&gt;不返回此行。<强> Here is a complete solution on SQL Fiddle 强>
示例数据
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
个。表格Pseudonyms
有200行,不同的PseudonymNames
:
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
首次尝试
起初我尝试了直接的方法。对于Users
中的每一行,我想从Pseudonyms
得到一个随机行:
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
;
事实证明,优化器太聪明了,这产生了一些随机的,但每个PseudonymName
都是User
,这不是我的预期:
ID UserName PseudonymName
1 UserName PseudonymName181
2 UserName PseudonymName181
...
999 UserName PseudonymName181
1000 UserName PseudonymName181
所以,我稍微调整了这个方法,并首先为Users
中的每一行生成一个随机数。然后,我使用生成的数字,使用Pseudonym
为ID
中的每一行Users
找到CROSS APPLY
。
CTE_Users
有一个额外的列,其中包含1到200的随机数。在CTE_Joined
中,我们为每个Pseudonyms
从User
中选择一行。
最后,我们UPDATE
原始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
<强> SQL Fiddle 强>