获取每行的随机数

时间:2015-06-11 21:21:39

标签: sql-server tsql

我有一个连续名字的表。对于每一行,我想生成一个随机名称。我写了以下查询:

$ npm init

问题是" ABS(CHECKSUM(NEWID()))%5"此查询的某个部分有时会返回多于1行,有时会返回0行。我必须遗漏一些东西,但我无法看到它。

如果我将查询更改为

BEGIN transaction t1

Create table TestingName
(NameID int,
 FirstName varchar(100),
 LastName varchar(100)
)

INSERT INTO TestingName
SELECT 0,'SpongeBob','SquarePants' 
UNION 
SELECT 1, 'Bugs', 'Bunny' 
UNION 
SELECT 2, 'Homer', 'Simpson' 
UNION 
SELECT 3, 'Mickey', 'Mouse' 
UNION 
SELECT 4, 'Fred', 'Flintstone'

SELECT FirstName from TestingName
WHERE NameID = ABS(CHECKSUM(NEWID())) % 5

ROLLBACK Transaction t1

然后一切正常,我每行得到一个随机数。

如果你接受上面的查询并将其粘贴到SQL管理工作室并运行第一个查询,你会看到我试图描述的内容。

最终更新查询看起来像

DECLARE @n int
set @n= ABS(CHECKSUM(NEWID())) % 5

SELECT FirstName from TestingName
WHERE NameID = @n

这不起作用,因为有时我会得到超过1行,有时候我没有行。

我错过了什么?

3 个答案:

答案 0 :(得分:1)

问题是您为每一行获取了不同的随机值。那是问题。此查询可能正在进行全表扫描。每行执行where子句 - 生成不同的随机数。

因此,您可能会获得一系列随机数,其中没有任何ID匹配。或者多个匹配的序列。平均而言,您只有一场比赛,但是您不想要平均而且#34;您需要保证。

这是您想要的rand(),每个查询只生成一个随机数:

SELECT FirstName
from TestingName
WHERE NameID = floor(rand() * 5);

这应该给你一个价值。

答案 1 :(得分:1)

为什么不使用前1名?

Select top 1 firstName
From testingName
Order by newId()

答案 2 :(得分:1)

这对我有用:

WITH
CTE
AS
(
    SELECT
        ID
        ,FName
        ,CAST(5 * (CAST(CRYPT_GEN_RANDOM(4) as int) / 4294967295.0 + 0.5) AS int) AS rr
    FROM
        dbo.TableWithABunchOfNames
)
,CTE_ForUpdate
AS
(
    SELECT
        CTE.ID
        , CTE.FName
        , dbo.TestingName.FirstName AS RandomName
    FROM
        CTE
        LEFT JOIN dbo.TestingName ON dbo.TestingName.NameID = CTE.rr
)
UPDATE CTE_ForUpdate
SET FName = RandomName
;

此解决方案取决于智能优化器的用途。

例如,如果我使用INNER JOIN而不是LEFT JOIN(这是此查询的正确选择),优化器会在连接循环外移动随机数的计算,最终结果将不是什么我们期待。

我在问题中创建了一个包含5行的表TestingName,并创建了一个包含100行的表TableWithABunchOfNames

以下是LEFT JOIN的执行计划。您可以看到计算随机数的Compute scalar在连接循环之前完成。您可以看到已更新了100行:

left join

以下是INNER JOIN的执行计划。您可以看到计算随机数的Compute scalar在连接循环之后和额外的过滤器完成。此查询可能不会更新TableWithABunchOfNames中的所有行,TableWithABunchOfNames中的某些行可能会多次更新。您可以看到Filter剩下102行而Stream aggregate只剩下69行。这意味着最终只更新了69行,并且某些行也有多个匹配(102 - 69 = 33)。

inner join

为了保证结果符合您的预期,您应为TableWithABunchOfNames中的每一行生成随机数,并明确记住结果,即实现上面显示的CTE。然后使用此临时结果加入表TestingName

您可以向TableWithABunchOfNames添加一列以存储生成的随机数,或将CTE保存到临时表或表变量。