我正在尝试将预定义值列表中的值随机插入到表中进行测试。我尝试使用此StackOverflow问题中找到的解决方案:
stackoverflow.com/.../update-sql-table-with-random-value-from-other-table
当我尝试这个时,所有插入的“随机”值对于所有3000条记录都完全相同。
当我运行实际选择随机行的查询部分时,每次我手动运行它时都会选择一个随机记录,所以我知道查询有效。我最好的猜测是发生了什么:
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())
答案 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