2009-09-23 41 views
7

Estoy intentando insertar al azar valores de una lista de valores predefinidos en una tabla para probar. He intentado utilizar la solución encontrada sobre esta cuestión StackOverflow:¿Cómo puedo insertar valores aleatorios en una tabla de SQL Server?

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

Cuando yo he intentado esto, todos mis valores "al azar" que se insertan son exactamente los mismos para todos los registros de 3000.

Cuando ejecuto la parte de la consulta que realmente selecciona la fila aleatoria, selecciona un registro aleatorio cada vez que lo ejecuto a mano, por lo que sé que la consulta funciona. Mis mejores conjeturas en cuanto a lo que está ocurriendo son:

  • SQL Server es la optimización de la SELECT alguna manera, no permitir que la subconsulta para ser evaluado más de una vez
  • semilla del valor aleatorio es el mismo en todos los registros de las actualizaciones de la consulta

Estoy atrapado en lo que son mis opciones. ¿Estoy haciendo algo mal, o hay otra forma en que debería estar haciendo esto?

Este es el código que estoy usando:

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

Esta pregunta/respuesta puede ser útil: http://stackoverflow.com/a/9039661/47226 –

Respuesta

14

Cuando el motor de consulta ve esto ...

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

... todo es así, "ooooh, una subconsulta escalar cachable, me ¡Voy a guardar eso en caché!

Necesita engañar al motor de búsqueda para que piense que no es cachable. El answer de jfar estaba cerca, pero el motor de búsqueda fue lo suficientemente inteligente como para ver la tautalogía de MyTable.MyColumn = MyTable.MyColumn, pero no es lo suficientemente inteligente como para ver a través de esto.

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 

Al incorporar a la tabla externa (MT) en la sub consulta, el motor de consulta supone tendrá que ser reevaluado subconsulta. Todo funcionará realmente, pero opté por la (supuesta) clave primaria de MyTable.Id ya que estaría indexada y agregaría muy poca sobrecarga.

Un cursor probablemente sea igual de rápido, pero ciertamente no es tan divertido.

+0

OK, no recuerdo si puedes hacer la UNIÓN INTERNA como la de SQL Server 2000, pero hay una forma de evitarlo, que utilicé todo el tiempo antes de ir al 2005. Eso fue hace muchos años para que yo lo recordara , aunque. Pero esto debería funcionar en 2005 y más tarde bien. –

+1

Eso funcionó de manera excelente. ¡Gracias! –

+2

+1 esto es fantástico, hay un pequeño error tipográfico, 'ON MT.Id = _MT.Id' debe ser' ON M.Id = _MT.Id' – Rippo

0

no tengo tiempo para comprobar esto ahora, pero mi instinto me dice que si tuviera que crear una función en el servidor para obtener el valor aleatorio que no lo optimizaría.

entonces tendría

UPDATE MyTable 
Set MyColumn = dbo.RANDOM_VALUE() 
0

No hay optimización pasando aquí.

Al usar una subconsulta que selecciona un solo valor, no hay nada que optimizar.

También puede tratar de poner una columna de la tabla de su actualización en la selección y ver si eso cambia algo. Esto puede desencadenar una evaluación para cada fila de MiTabla

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

Lo probé. No tuve suerte cambiando los resultados. –

2

el uso de una combinación cruzada para generar datos aleatorios

+0

¿Tiene un ejemplo que podría usar? No estoy familiarizado con la idea de las combinaciones cruzadas. –

0

me ocurrió una solución que es un poco de un truco y muy ineficiente (10 ~ segundos a actualizar 3000 registros). Debido a que esto se está utilizando para generar datos de prueba, no tengo que preocuparme por la velocidad.

En esta solución, iterar sobre cada fila en la tabla y actualizar los valores de una fila a la vez. Parece que funciona:

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

he tenido un juego con esto, y encontré una manera bastante hacky hacerlo con el uso de una variable de tabla intermedia.

Una vez @randomStuff está configurado, vamos a hacer esto (nota en mi caso, @mitabla es una variable de tabla, los ajustes correspondientes para su mesa normal):

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 

lo que en este punto, tenemos una tabla intermedia con cada combinación de (id mytable, valor aleatorio) y un valor de clasificación aleatorio para cada fila específica de esa combinación.Entonces

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

Este es un viejo truco que borra todas las filas para un determinado MyTable.id a excepción de la que tiene el menor valor de ordenación - unirse a la tabla consigo misma, donde el valor es más pequeño, y elimine cualquier lugar, tales una unión tuvo éxito. Esto simplemente deja atrás el valor más bajo. Así que para cada MyTable.id, sólo tenemos un valor (al azar) se fue .. Entonces sólo tiene que enchufar de nuevo en la mesa:

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

Y ya está!

me dijeron era hacky ...

Cuestiones relacionadas