如何将随机值插入SQL Server表?

时间:2009-09-23 19:40:25

标签: sql sql-server tsql random

我正在尝试将预定义值列表中的值随机插入到表中进行测试。我尝试使用此StackOverflow问题中找到的解决方案:

stackoverflow.com/.../update-sql-table-with-random-value-from-other-table

当我尝试这个时,所有插入的“随机”值对于所有3000条记录都完全相同。

当我运行实际选择随机行的查询部分时,每次我手动运行它时都会选择一个随机记录,所以我知道查询有效。我最好的猜测是发生了什么:

  • SQL Server正在以某种方式优化SELECT,不允许对子查询进行多次评估
  • 随机值的种子在查询更新的每条记录上都是相同的

我坚持我的选择。我做错了什么,或者我还有另一种方法吗?

这是我正在使用的代码:

DECLARE @randomStuff TABLE ([id] INT, [val] VARCHAR(100))

INSERT INTO @randomStuff ([id], [val]) 
VALUES ( 1,  'Test Value 1' )
INSERT INTO @randomStuff ([id], [val])
VALUES ( 2,  'Test Value 2' )
INSERT INTO @randomStuff ([id], [val])
VALUES ( 3,  'Test Value 3' )
INSERT INTO @randomStuff ([id], [val])
VALUES ( 4,  'Test Value 4' )
INSERT INTO @randomStuff ([id], [val])
VALUES ( 5,  'Test Value 5' )
INSERT INTO @randomStuff ([id], [val])
VALUES ( 6,  null )
INSERT INTO @randomStuff ([id], [val])
VALUES ( 7,  null )
INSERT INTO @randomStuff ([id], [val])
VALUES ( 8,  null )
INSERT INTO @randomStuff ([id], [val])
VALUES ( 9,  null )
INSERT INTO @randomStuff ([id], [val])
VALUES ( 10, null )

UPDATE MyTable
SET MyColumn = (SELECT TOP 1 [val] FROM @randomStuff ORDER BY NEWID())

6 个答案:

答案 0 :(得分:15)

当查询引擎看到这个......

(SELECT TOP 1 [val] FROM @randomStuff ORDER BY NEWID())

......这就像是,“哦,一个可缓存的标量子查询,我要缓存它!”

你需要欺骗查询引擎认为它是不可缓存的。 jfar的answer很接近,但是查询引擎非常聪明,可以看到MyTable.MyColumn = MyTable.MyColumn的简写,但是看起来不够聪明。

UPDATE MyTable
   SET MyColumn = (SELECT TOP 1 val
                     FROM @randomStuff r
                          INNER JOIN MyTable _MT
                                  ON M.Id = _MT.Id
                    ORDER BY NEWID())
 FROM MyTable M

通过将外部表(MT)引入子查询,查询引擎假定需要重新评估子查询。任何东西都可以正常工作,但是我选择了MyTable.Id的(假设的)主键,因为它被编入索引并且会增加很少的开销。

光标可能同样快,但肯定不那么有趣。

答案 1 :(得分:2)

使用交叉连接生成随机数据

答案 2 :(得分:1)

我玩过这个游戏,发现使用中间表变量做一个相当粗俗的方法。

一旦设置了@randomStuff,我们就这样做了(注意在我的情况下,@ MyTable是一个表变量,相应地调整你的普通表):

DECLARE @randomMappings TABLE (id INT, val VARCHAR(100), sorter UNIQUEIDENTIFIER)

INSERT INTO @randomMappings 
SELECT M.id, val, NEWID() AS sort 
FROM @MyTable AS M 
CROSS JOIN @randomstuff

所以在这一点上,我们有一个中间表,其中包含(mytable id,random value)的每个组合,以及特定于该组合的每一行的随机排序值。然后

DELETE others FROM @randomMappings AS others 
INNER JOIN @randomMappings AS lower 
ON (lower.id = others.id) AND (lower.sorter < others.sorter)

这是一个旧技巧,删除给定MyTable.id的所有行,除了具有较低排序值的行 - 将表连接到值较小的表,并删除任何此类连接成功的地方。这只留下了最低价值。因此,对于每个MyTable.id,我们只剩下一个(随机)值。然后我们将其重新插入表中:

UPDATE @MyTable
SET MyColumn = random.val
FROM @MyTable m, @randomMappings AS random
WHERE (random.id = m.id)

你已经完成了!

这是hacky ......

答案 3 :(得分:0)

我现在没时间检查这个,但我的直觉告诉我,如果你要在服务器上创建一个函数来获取随机值,它就不会优化它。

然后你会有

UPDATE MyTable
Set MyColumn = dbo.RANDOM_VALUE()

答案 4 :(得分:0)

这里没有优化。

您使用选择单个值的子查询,无需优化。

您还可以尝试从更新的表格中选择一个列,然后查看是否有任何更改。这可能会触发对MyTable中每一行的评估

UPDATE MyTable
SET MyColumn = (SELECT TOP 1 [val] FROM @randomStuff ORDER BY NEWID()
    WHERE MyTable.MyColumn = MyTable.MyColumn )

答案 5 :(得分:0)

我提出了一个有点破解且效率非常低的解决方案(更新3000条记录的时间为10秒)。因为这用于生成测试数据,所以我不必关心速度。

在此解决方案中,我迭代表中的每一行并一次更新一行值。它似乎有效:

DECLARE @rows INT 
DECLARE @currentRow INT

SELECT @rows = COUNT(*) FROM dbo.MyTable
SET @currentRow = 1

WHILE @currentRow < @rows
BEGIN 

UPDATE MyTable
SET MyColumn = (SELECT TOP 1 [val] FROM @randomStuff ORDER BY NEWID())
WHERE MyPrimaryKey = (SELECT b.MyPrimaryKey
 FROM(SELECT a.MyPrimaryKey, ROW_NUMBER() OVER (ORDER BY MyPrimaryKey) AS rownumber
      FROM MyTable a) AS b
 WHERE @currentRow = b.rownumber
)

SET @currentRow = @currentRow + 1
END