Как вставить случайные значения в таблицу SQL Server?

Я пытаюсь случайным образом вставлять значения из списка предопределенных значений в таблицу для тестирования. Я попытался использовать решение, найденное в этом вопросе StackOverflow:

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

когда я попробовал это, все мои" случайные " значения, которые вставлены, точно такие же для всех 3000 записей.

когда я запускаю часть запроса, которая фактически выбирает случайную строку, она выбирает случайную запись каждый раз, когда я запускаю ее вручную, поэтому я знаю, что запрос работает. Мои лучшие догадки о том, что происходит:

  • SQL Server оптимизирует SELECT как-то, не позволяя подзапросу оцениваться более одного раза
  • семя случайного значения одинаково на каждой записи обновления запроса

Я застрял на том, что у меня варианты. Я делаю что-то не так, или есть другой способ сделать это?

Это код, который я использую:

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())

6 ответов


когда механизм запросов видит это...

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

... это все похоже на: "Оооо, доступный скалярный подзапрос, я собираюсь кэшировать это!"

вам нужно обмануть механизм запросов, думая, что он недоступен. jfar это ответ была близка, но двигатель запрос был достаточно умен, чтобы видеть tautalogy из MyTable.MyColumn = MyTable.MyColumn, но он недостаточно умен, чтобы видеть сквозь это.

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

введя внешнюю таблицу (MT) в подзапрос, механизм запросов предполагается, что подзапрос необходимо будет переоценить. Все будет работать действительно, но я пошел с (предполагаемым) первичным ключом MyTable.Id, так как он будет индексироваться и добавит очень мало накладных расходов.

курсор, вероятно, будет так же быстро, но уже не так весело.


использовать перекрестное соединение для генерации случайных данных


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

как только @randomStuff настроен, мы делаем это (обратите внимание, что в моем случае @MyTable-переменная таблицы, отрегулируйте соответственно для вашей обычной таблицы):

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

Итак, на данный момент у нас есть промежуточная таблица с каждой комбинацией (mytable id, случайное значение) и случайное значение сортировки для каждой строки, специфичной для этой комбинации. Тогда

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

Это старый трюк, который удаляет все строки для данной MyTable.id, за исключением того, с более низким значением сортировки-присоедините таблицу к себе, где значение меньше,и удалите все, где такое соединение удалось. Это просто оставляет после себя самое низкое значение. Так для каждого MyTable.id, у нас осталось только одно (случайное) значение.. Затем мы просто подключаем его обратно в таблицу:

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

и ты молодец!

Я сказал он был хакки...


У меня нет времени проверять это прямо сейчас, но моя интуиция говорит мне, что если бы вы создали функцию на сервере, чтобы получить случайное значение, оно не оптимизировало бы ее.

тогда вы бы

UPDATE MyTable
Set MyColumn = dbo.RANDOM_VALUE()

здесь не происходит никакой оптимизации.

при использовании подзапроса, который выбирает одно значение, нечего оптимизировать.

вы также можете попробовать поместить столбец из таблицы вашего обновления в select и посмотреть, изменится ли что-нибудь. Это может вызвать оценку для каждой строки в MyTable

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

Я придумал решение, которое является хаком и очень неэффективно (10~ секунд для обновления 3000 записей). Поскольку это используется для генерации тестовых данных, мне не нужно беспокоиться о скорости.

в этом решении я перебираю каждую строку в таблице и обновляю значения по одной строке за раз. Кажется, работает:

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