2009-09-23 92 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) 

大功告成!

這是哈克......