2010-03-04 88 views
9

我想通過一張表並隨機清除一些數據。我正在做一些數據隨機化,把真正的名字變成僞造的名字等等。所涉及的表中有一列有大約40%的時間空列。我的名字randomizer應用程序可以在它指定新的名字時在它的某個位置投擲硬幣。但我寧願最後這樣做:隨機刪除一些數據。如何隨機更新行?

我有這樣的代碼,這是不行的,但看起來確實像它應該對我說:

Use MyDb 
go 

CREATE VIEW vRandNumber 
AS 
SELECT RAND() as RandNumber 

go 

CREATE FUNCTION RandNumber() 
RETURNS float 
AS 
    BEGIN 
    RETURN (SELECT RandNumber FROM vRandNumber) 
    END 

go 

select dbo.RandNumber() 

update names set nickname = null 
where ((select dbo.RandNumber()) > 0.5) 

當我運行RandNumber功能也很好,很多隨機的。但是當我進行更新時,它會在一半時間更新所有行,而另一半則不更新行。

我希望它在每次運行腳本時更新隨機數的行。我真的認爲像RandNumber這樣的函數會爲表中的每一行運行一次。顯然不是。

這可能沒有循環,沒有控制檯應用程序?

編輯:我也嘗試了幾個RAND()的變體直接在哪裏得到了相同的結果。

+1

您可以使用:WHERE RAND()> 0.5'。不需要視圖和功能。 – 2010-03-04 15:40:11

+0

@OMG:嘗試過,結果相同。 – jcollum 2010-03-04 15:42:49

+0

10k查看,7 upvotes; smh – jcollum 2017-03-15 16:40:57

回答

22

假設你的名字表中有稱爲ID的主鍵字段,這將抵消的暱稱中的行的隨機50%:

update dbo.Names set Nickname = null where Id in 
(
    select top 50 percent id from dbo.Names order by NEWID() 
) 
+0

這有效,但效率低下 - 爲每行生成一個新的GUID將比生成該行的單個整數花費更長的時間。 – 2010-03-04 15:55:07

+7

Pfft,來吧!超級性能真的是這種用例的一個問題嗎?談論過早的優化。 我剛剛在182,770行的桌子上試過這種方法,它在11秒內運行。 – 2010-03-04 16:04:25

+0

嗯,它很接近。但它會一直更新相同數量的行。我想我需要一個循環來更新隨機數的行。 – jcollum 2010-03-04 16:08:10

1

RandNumber是一個函數。 SQL中的函數必須每次輸出相同的結果才能進行相同的輸入,除非底層數據庫數據已更改。這是一個函數的數學定義(與正常的編程語言如何對待一個「函數」,它更像一個函數式構造)相反。

由於您的函數的結果決不會在更新語句(這是一個原子操作)期間發生變化,查詢的查詢計劃編譯器只會調用RandNumber一次,然後緩存結果。

您或許可以直接在查詢中引用RAND,但如果仍然無法正常工作,則必須在存儲過程中迭代執行此操作。

+0

好的,但我聽說當你做一個getdate()作爲select中的where部分時,getdate()將爲select中的每一行運行一次。這是不正確的? – jcollum 2010-03-04 15:44:03

+3

在MS-SQL函數中可以是確定性的(如您所述)或非確定性請參閱http://msdn.microsoft.com/en-us/library/aa214775%28SQL.80%29.aspx RAND函數是非確定性的。 – 2010-03-04 15:49:01

+0

在這種情況下,幾乎每個函數都會確定性地運行,因爲'update'是原子的。因此,即使調用了一個通常不確定的函數,它的模式綁定特性也會被底層數據在查詢執行期間無法更改的事實所抵銷。我認爲這**可以迭代地完成,除非你可以強制查詢計劃編譯器將該函數視爲非確定性函數,儘管它不想這樣做。 – 2010-03-04 15:52:45

0

如何

update names set nickname = null 
where abs(checksum(nickname) % 2) = 0 
+0

不,我使用你的位置運行更新,並且每次運行時都會更新所有行。 – jcollum 2010-03-04 15:47:47

+0

@jcollumn,奇/偶是怎麼做的? – Hogan 2010-03-04 15:48:55

+0

@jcollumn - 好的我測試了這一個,它會工作...警告空名稱或「常量」名稱都會做同樣的事情,所以它不是完全隨機的。 – Hogan 2010-03-04 15:53:57

0

嘗試這樣的事:

WHERE DATEPART(ms,CreateDate)>500 

其中「CREATEDATE」是一列已經在擁有它的實際日期和時間的表。裏邊反毫秒應該是相當隨機

編輯 這裏的另一種方法:

DECLARE @YourTable table (RowID int, RowValue varchar(5)) 
INSERT INTO @YourTable VALUES (1,'one') 
INSERT INTO @YourTable VALUES (2,'two') 
INSERT INTO @YourTable VALUES (3,'three') 

SELECT 
    RAND(row_number() over(order by RowID)+DATEPART(ms,GETDATE())),* 
    FROM @YourTable 

輸出運行1:

     RowID  RowValue 
---------------------- ----------- -------- 
0.716200609189072  1   one 
0.71621924216033  2   two 
0.716237875131588  3   three 

(3列(S)的影響)

輸出運行2:

     RowID  RowValue 
---------------------- ----------- -------- 
0.727007732518828  1   one 
0.727026365490086  2   two 
0.727044998461344  3   three 

(3 row(s) affected) 
0

RAND()(和GETDATE/CURRENT_TIMESTAMP)被評估一次每聲明。你需要一些方法來解決這個問題。一種方法是(如果你有一個方便的行值整數,例如一個ID列),就是調用RAND(ID)。

+0

還只調用過一次。 – Hogan 2010-03-04 15:57:54

+0

恩,沒有。 RAND(ID),其中ID在每行的基礎上變化,每行調用一次。 – 2010-03-04 18:50:40

0

RAND()在查詢中持續存在。

SELECT RAND() 
FROM names 

會給你一組相等的數字。

你需要做這樣的事情:

WITH q AS 
     (
     SELECT *, 
       ABS(CHECKSUM(NEWID())) % 2 AS r 
     FROM names 
     ) 
UPDATE q 
SET  nickname = NULL 
WHERE r = 0 
0

這是正常的分佈(不是隨機的)解決方案。它根據Vehicle.ID % 10 + 1 = branch_number分配車輛到分支:

; WITH mytbl AS (
    SELECT TOP 10 *, ROW_NUMBER() OVER (ORDER BY NEWID()) num 
    FROM Branch 
    ORDER BY num 
) 

UPDATE v 
SET BranchID = mytbl.ID 
FROM Vehicle v 
    INNER JOIN mytbl ON mytbl.num = v.ID % 10 + 1 

SELECT BranchID, COUNT(*) FROM Vehicle GROUP BY BranchID