Эффективно рандомизировать (перемешивать) данные в таблице Sql Server - PullRequest
3 голосов
/ 02 августа 2011

У меня есть таблица с данными, которые я должен рандомизировать.Под рандомизацией я подразумеваю использование данных из случайной строки для обновления другой строки в этом же столбце.Проблема в том, что сама таблица большая (более 2 000 000 строк).

Я написал фрагмент кода, в котором используется цикл while, но он идет медленно.

У кого-нибудь есть предложения?о более эффективном способе достижения рандомизации?

Ответы [ 4 ]

4 голосов
/ 02 августа 2011

Для обновления строк будет значительное время обработки (CPU + I / O) из обновлений.

Вы измерили относительную стоимость рандомизации строк по сравнению с выполнением обновлений?

Все, что вам нужно сделать, это выбрать случайные строки, вот эффективный метод, чтобы выбрать случайную выборку строк (в данном случае 1% строк)

SELECT * FROM myTable
WHERE 0.01 >= CAST(CHECKSUM(NEWID(), pkID) & 0x7fffffff AS float) / CAST (0x7fffffff AS int)

, где pkID это вашстолбец первичного ключа.

Эта публикация может представлять интерес:

2 голосов
/ 04 октября 2014

На основе ответа Mitch Wheats, связанного с этой статьей о данных скремблирования вы можете сделать что-то подобное, чтобы перемешать несколько полей, вы не ограничены только идентификаторами:

;WITH Randomize AS 
( 
SELECT ROW_NUMBER() OVER (ORDER BY [UserID]) AS orig_rownum, 
      ROW_NUMBER() OVER (ORDER BY NewId()) AS new_rownum, 
      * 
FROM [UserTable]
) 
UPDATE T1 
   SET [UserID] = T2.[UserID]
      ,[FirstName] = T2.[FirstName]
      ,[LastName] = T2.[LastName]
      ,[AddressLine1] =  T2.[AddressLine1]
      ,[AddressLine2] =  T2.[AddressLine2]
      ,[AddressLine3] =  T2.[AddressLine3]
      ,[City] = T2.[City]
      ,[State] = T2.[State]
      ,[Pincode] = T2.[Pincode]
      ,[PhoneNumber] = T2.[PhoneNumber]
      ,[MobileNumber] = T2.[MobileNumber]
      ,[Email] = T2.[Email]
      ,[Status] = T2.[Status] 
FROM Randomize T1 
      join Randomize T2 on T1.orig_rownum = T2.new_rownum 
;

Таким образом, вы не ограничены только этим, как показано в статье:

;WITH Randomize AS 
( 
SELECT ROW_NUMBER() OVER (ORDER BY Id) AS orig_rownum, 
      ROW_NUMBER() OVER (ORDER BY NewId()) AS new_rownum, 
      * 
FROM [MyTable]
) 
UPDATE T1 SET Id = T2.Id 
FROM Randomize T1 
      join Randomize T2 on T1.orig_rownum = T2.new_rownum 
;

Опасность для этого подхода - количество данных, которые вы настраиваете.Использование CTE помещает все эти вещи в память, поэтому пока я обнаружил, что это довольно быстро (19 секунд для таблицы строк по 500 КБ).Вы будете осторожны, если у вас есть таблица с миллионами записей.Вы должны учитывать, сколько данных на самом деле необходимо или является хорошей выборкой для тестирования и разработки.

2 голосов
/ 02 августа 2011

Перемешать данные в 10 столбцах, чтобы заменить 10 значений в строке другими значениями из других строк будет стоить .

Вы должны прочитать 2 миллиона строк 10 раз.

SELECT будет

SELECT
    FirstName, LastName, VIN, ...
FROM
    (SELECT FirstName FROM MyTable ORDER BY NEWID()) FirstName
    JOIN
    (SELECT LastName FROM MyTable ORDER BY NEWID()) LastName ON 1=1
    JOIN
    (SELECT VIN FROM MyTable ORDER BY NEWID()) VIN ON 1=1
    JOIN
    ...

Я бы тоже не обновился, я бы создал новую таблицу

SELECT
    FirstName, LastName, VIN, ...
INTO
    StagingTable
FROM
    (SELECT FirstName FROM MyTable ORDER BY NEWID()) FirstName
    JOIN
    (SELECT LastName FROM MyTable ORDER BY NEWID()) LastName ON 1=1
    JOIN
    (SELECT VIN FROM MyTable ORDER BY NEWID()) VIN ON 1=1
    JOIN
    ...

Затем добавьте ключи и т. Д., Удалите старую таблицу, переименуйте ее. Или используйте SYNONYM , чтобы указать на новую таблицу

Если вы хотите обновить, я бы сделал это так. или разбить его на 10 обновлений.

UPDATE
   M
SET
   Firstname = FirstName.FirstName,
   LastName = LastName.LastName,
   ...
FROM
    MyTable M
    JOIN 
    (SELECT FirstName FROM MyTable ORDER BY NEWID()) FirstName ON 1=1
    JOIN
    (SELECT LastName FROM MyTable ORDER BY NEWID()) LastName ON 1=1
    JOIN
    (SELECT VIN FROM MyTable ORDER BY NEWID()) VIN ON 1=1
    JOIN
    ...
1 голос
/ 23 июня 2016

Я объединил ответы, которые я нашел выше, в один запрос, который снова рандомизирует каждый столбец, в результате чего получаются полностью рандомизированные записи

UPDATE MyTable SET
  columnA = columnA.newValue,
  columnB = columnB.newValue,
  -- Optionally, for maintaining a group of values like street, zip, city in an address
  columnC = columnGroup.columnC,
  columnD = columnGroup.columnD,
  columnE = columnGroup.columnE
FROM MyTable
INNER JOIN (
  SELECT ROW_NUMBER() OVER (ORDER BY id) AS rn, id FROM MyTable
) AS PKrows ON MyTable.id = PKrows.id
-- repeat the following JOIN for each column you want to randomize
INNER JOIN (
  SELECT ROW_NUMBER() OVER (ORDER BY NEWID()) AS rn, columnA AS newValue FROM MyTable
) AS columnA ON PKrows.rn = columnA.rn
INNER JOIN (
  SELECT ROW_NUMBER() OVER (ORDER BY NEWID()) AS rn, columnB AS newValue FROM MyTable
) AS columnB ON PKrows.rn = columnB.rn

-- Optionally, if you want to maintain a group of values spread out over several columns
INNER JOIN (
  SELECT ROW_NUMBER() OVER (ORDER BY NEWID()) AS rn, columnC, columnD, columnE FROM MyTable
) AS columnGroup ON PKrows.rn = columnGroup.rn

Этот запрос занял 8 секунд для таблицы из 10 000 строк, тасовавшей 8 столбцов на компьютере под управлением Windows 2008 R2 с 16 ГБ памяти и 4 ядрами XEON @ 2,93 ГГц

...