2009-09-23 45 views
7

我想从预定义值列表中随机插入值到测试表中。我尝试使用这个StackOverflow的问题找到了解决办法:如何将随机值插入到SQL Server表中?

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()) 
+0

这个问题可能有用:http://stackoverflow.com/a/9039661/47226 – 2013-04-29 20:33:06

回答

14

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

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

...这一切都喜欢,“OOOOH,一个超高速缓存标量子查询,我我会缓存的!“

您需要欺骗查询引擎认为它是不可缓存的。 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的(假设)主键,因为它会被索引,并且会增加很少的开销。

游标可能会一样快,但肯定不是那么有趣。

+0

好吧,我不记得你是否可以像SQL Server 2000那样进行INNER JOIN,但是有一种解决方法,我在2005年之前一直使用这种方法。那是多年前我才能记住的,但是。但这应该在2005年工作,后来就好了。 – 2009-09-24 06:26:57

+1

工作出色。谢谢! – 2009-09-24 13:43:50

+2

+1这真是太棒了,只是有一个小的错字,'ON MT.Id = _MT.Id'应该是'ON M.Id = _MT.Id' – Rippo 2011-10-12 08:40:34

0

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

那么你就必须

UPDATE MyTable 
Set MyColumn = dbo.RANDOM_VALUE() 
0

没有优化怎么回事。

您使用选择单个值的子查询,有什么优化。

您也可以尝试从select中更新表中的列并查看是否改变了任何内容。这可能触发评估每一行中的MyTable

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

我刚刚试过。没有运气改变结果。 – 2009-09-23 20:00:43

2

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

+0

你有我可以使用的例子吗?我不熟悉交叉连接的想法。 – 2009-09-23 20:54:20

0

我想出了一个解决方案,它是一个黑客位和效率非常低(10〜秒,更新3000条记录)。因为这是用来生成测试数据的,所以我不必担心速度。

在此解决方案中,我遍历表中的每一行并每次更新一行的值。看来工作:

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 
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,随机值)的每种组合,以及针对该组合的每行的随机排序值。然后

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) 

大功告成!

这是哈克......