Как вставить случайные значения в таблицу 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