2009-09-23 18 views
7

Sto provando a inserire in modo casuale i valori da un elenco di valori predefiniti in una tabella per il test. Ho provato ad utilizzare la soluzione trovata su questa questione StackOverflow:Come posso inserire valori casuali in una tabella di SQL Server?

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

Quando ho ho provato questo, tutti i miei valori "casuali" che vengono inseriti sono esattamente gli stessi per tutti i 3000 record.

Quando eseguo la parte della query che seleziona effettivamente la riga casuale, esso seleziona un record casuale ogni volta che lo eseguo a mano, quindi so che la query funziona. Le mie migliori congetture su ciò che sta accadendo sono:

  • SQL Server sta ottimizzando il SELECT in qualche modo, non permettendo la sottoquery da valutare più di una volta
  • seme del valore casuale è la stessa su tutti i record degli aggiornamenti di query

Sono bloccato su quali sono le mie opzioni. Sto facendo qualcosa di sbagliato, o c'è un altro modo in cui dovrei fare questo?

Questo è il codice che sto utilizzando:

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

Questa domanda/risposta può essere utile: http://stackoverflow.com/a/9039661/47226 –

risposta

14

Quando il motore di query vede questo ...

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

... è tutto come, "Ooooh, un cachable subquery scalare, ho me ne accorgero '!

È necessario ingannare il motore di query nel pensare che non sia intercambiabile. jfar's answer era vicino, ma il motore di query era abbastanza intelligente da vedere la tautalogia di MyTable.MyColumn = MyTable.MyColumn, ma non è abbastanza intelligente da vedere attraverso questo.

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 

Portando nella tabella esterna (MT) nella subquery, il motore di query assume subquery dovrà essere nuovamente valutata. Qualunque cosa funzionerà davvero, ma sono andato con la chiave primaria (presunta) di MyTable.Id dato che sarebbe stata indicizzata e avrebbe aggiunto un piccolo sovraccarico.

Un cursore sarebbe probabilmente altrettanto veloce, ma non è certamente così divertente.

+0

OK, non ricordo se è possibile eseguire l'INTERNO INNER come quello in SQL Server 2000, ma c'è un modo per aggirarlo, che ho usato tutto il tempo prima di andare nel 2005. Troppi anni fa per me da ricordare , anche se. Ma questo dovrebbe funzionare nel 2005 e in seguito va bene. –

+1

Che ha funzionato in modo eccellente. Grazie! –

+2

+1 questo è fantastico, c'è un piccolo errore di battitura, 'ON MT.Id = _MT.Id' dovrebbe essere' ON M.Id = _MT.Id' – Rippo

0

Non ho tempo di controllare questo proprio ora, ma il mio istinto mi dice che se si dovesse creare una funzione sul server per ottenere il valore casuale che non lo ottimizzerebbe.

allora si avrebbe

UPDATE MyTable 
Set MyColumn = dbo.RANDOM_VALUE() 
0

Non v'è alcuna ottimizzazione succedendo qui.

Se si utilizza una sottoquery che seleziona un valore singolo, non c'è nulla da ottimizzare.

Puoi anche provare a mettere una colonna dalla tabella il tuo aggiornamento nella selezione e vedere se questo cambia qualcosa. Che possono scatenare una valutazione per ogni riga MyTable

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

Ho appena provato. Non ha avuto fortuna a cambiare i risultati. –

2

uso un cross join per generare dati casuali

+0

Hai un esempio che potrei usare? Non ho familiarità con l'idea di cross join. –

0

mi si avvicinò con una soluzione che è un po 'di hack e molto inefficiente (10 ~ secondi a aggiornare 3000 record). Poiché questo viene utilizzato per generare dati di test, non devo tuttavia preoccuparmi della velocità.

In questa soluzione, iterò su ogni riga della tabella e aggiorno i valori una riga alla volta. Sembra funzionare:

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

ho avuto un gioco con questo, e ha trovato un modo piuttosto hacky a che fare con l'uso di una variabile tabella intermedia.

volta @randomStuff è impostato, facciamo questo (nota nel mio caso, @MyTable è una variabile di tabella, regolare di conseguenza per la vostra tavola normale):

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 

così a questo punto, abbiamo un tabella intermedia con ogni combinazione di (id mytable, valore casuale) e un valore di ordinamento casuale per ogni riga specifica per quella combinazione.Poi

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

Questo è un vecchio trucco che cancella tutte le righe per un dato MyTable.id ad eccezione di quello con il valore più basso tipo - si uniscono al tavolo a se stesso, dove il valore è più piccolo, ed eliminare ogni dove tale un join è riuscito. Questo lascia solo il valore più basso. Quindi, per ogni MyTable.id, non ci resta che un valore (casuale) a sinistra .. Poi abbiamo appena ricollegarlo nella tabella:

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

E il gioco è fatto!

ho detto era hacky ...

Problemi correlati