2010-03-04 18 views
9

Me gustaría pasar por una tabla y borrar aleatoriamente algunos de los datos. Estoy haciendo algunos datos aleatorizados, convirtiendo los nombres reales en falsos, etc. Bueno, una de las tablas involucradas tiene una columna que es nula aproximadamente el 40% del tiempo. Mi nombre, la aplicación de aleatorizador, podría hacer una moneda en algún lugar cuando asigna nuevos nombres. Pero prefiero simplemente hacerlo al final: pode aleatoriamente algunos de los datos.¿cómo puedo actualizar las filas al azar?

tengo este código, que no funciona, pero seguro que se ve como que me debe a:

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) 

Cuando ejecuto la función RandNumber está bien, un montón al azar. Pero cuando hago la actualización, actualizo todas las filas la mitad del tiempo y ninguna de las filas la otra mitad del tiempo.

Quiero que actualice un número aleatorio de filas cada vez que ejecuto el script. Realmente pensé que una función como RandNumber se ejecutaría una vez por cada fila en la tabla. Aparentemente no.

¿Esto es posible sin un bucle y sin una aplicación de consola?

Editar: También lo intenté con un par de variaciones de RAND() en donde directamente y obtuve los mismos resultados.

+1

Puede usar simplemente: 'WHERE RAND()> 0.5'. No hay necesidad de la vista y la función. –

+0

@OMG: Intenté eso, mismo resultado. – jcollum

+0

10k vistas, 7 votos hacia arriba; smh – jcollum

Respuesta

22

Suponiendo mesa sus nombres tiene un campo de clave principal llamado id, esto anularía el apodo de forma aleatoria 50 por ciento de las filas:

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

Esto funciona pero es ineficiente: la generación de un nuevo GUID para cada fila demorará un poco más que generar un único entero para la fila. –

+7

Pfft, vamos! ¿El súper rendimiento es realmente un problema para este tipo de caso de uso? Hable sobre la optimización prematura. Acabo de probar este enfoque en una mesa con 182,770 filas y se ejecutó en 11 segundos. –

+0

Bueno, está cerca. Pero siempre actualizará la misma cantidad de filas. Creo que necesito un bucle para actualizar un número aleatorio de filas. – jcollum

1

RandNumber es una función. Las funciones en SQL deben generar el mismo resultado cada vez para la misma entrada, a menos que los datos subyacentes de la base de datos hayan cambiado. Esta es la definición matemática de una función (a diferencia de cómo un lenguaje de programación normal trata una "función", que es más una construcción similar a una función).

Como el resultado de su función nunca debe cambiar durante una instrucción de actualización (que es una operación atómica) el compilador del plan de consulta solo llama al RandNumber y luego almacena el resultado en caché.

Puede que salga con la simple referencia RAND directamente en su consulta, pero si eso todavía no funciona, tendrá que hacerlo iterativamente en un procedimiento almacenado.

+0

OK, pero he escuchado que cuando haces un getdate() como parte de a donde en un seleccionar que getdate() se ejecutará una vez por cada fila en la selección. ¿No es eso correcto? – jcollum

+3

En MS-SQL Las funciones pueden ser deterministas (como usted lo describió) o no deterministas ver http://msdn.microsoft.com/en-us/library/aa214775%28SQL.80%29.aspx la función RAND no es determinista . –

+0

En este caso, casi todas las funciones se ejecutarán de manera determinista porque 'update' es atómico. Por lo tanto, incluso si se llama a una función normalmente no determinista, la naturaleza vinculada al esquema se niega por el hecho de que los datos subyacentes no pueden cambiar durante la ejecución de la consulta. Creo que esto ** tiene ** que hacerse iterativamente a menos que pueda obligar al compilador del plan de consulta a tratar la función como no determinista a pesar del hecho de que no querrá hacerlo. –

0

¿Qué tal

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

No, ejecuté la actualización con su ubicación y actualizó todas las filas cada vez que la ejecuté. – jcollum

+0

@jcollumn, ¿cómo funciona el impar/par? – Hogan

+0

@jcollumn - ok He probado este, funcionará ... el nombre nulo de advertencia o los nombres "constantes" harán lo mismo, por lo que no es totalmente aleatorio. – Hogan

0

probar algo como esto:

WHERE DATEPART(ms,CreateDate)>500 

donde "CreateDate" es una columna que ya están en la mesa que tiene una fecha y la hora real en ella.Lla milisegundos debe ser bastante aleatoria

EDITAR aquí hay otro enfoque:

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 

salida RUN 1:

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

(3 fila (s) affected) de ejecución

SALIDA 2:

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

(3 row(s) affected) 
0

RAND() (y GetDate/CURRENT_TIMESTAMP) se evalúa una vez por declaración. Necesitas de alguna manera para evitar eso. Una forma es (si tiene un entero valioso de fila conveniente, por ejemplo, una columna de ID), es llamar a RAND (ID) en su lugar.

+0

Todavía solo se llama una vez. – Hogan

+0

Um, no. RAND (ID), donde ID varía según una fila, se llama una vez por fila. –

0

RAND() persiste en una consulta.

SELECT RAND() 
FROM names 

le dará un conjunto de números iguales.

que tiene que hacer algo como esto:

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

Esta es la solución normal de distribución (no al azar). Asigna vehículos a las sucursales de acuerdo con 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 
Cuestiones relacionadas