Я пытаюсь выбрать случайную 10% выборку из небольшой таблицы. Я думал, что просто воспользуюсь функцией RAND () и выберу те строки, в которых случайное число меньше 0,10:
SELECT * FROM SomeTable
WHERE SomeColumn='SomeCondition' AND
RAND() < 0.10
Но вскоре я обнаружил, что RAND () всегда возвращает одно и то же число! Напоминает мне этот xkcd мультфильм.

Хорошо, без проблем, функция RAND принимает начальное значение. Я буду запускать этот запрос периодически, и я хочу, чтобы он давал разные результаты, если я запустил его в другой день, поэтому я заполняю его комбинацией даты и уникального идентификатора строки:
SELECT * FROM SomeTable
WHERE SomeColumn='SomeCondition' AND
RAND(CAST(GETDATE) AS INTEGER) + RowID) < 0.10
Я все еще не получаю результатов! Когда я показываю случайные числа, возвращаемые RAND, я обнаруживаю, что все они находятся в узком диапазоне. Похоже, что получение случайного числа из RAND требует, чтобы вы использовали случайное начальное число. Если бы у меня изначально было случайное семя, мне бы не понадобилось случайное число!
Я видел предыдущие обсуждения, связанные с этой проблемой:
Случайная сортировка SQL Server
Как запросить случайную строку в SQL?
Они мне не помогают. TABLESAMPLE работает на уровне страницы, что отлично подходит для большой таблицы, но не для маленькой, и похоже, что это применяется до предложения WHERE. TOP с NEWID не работает, потому что я не знаю заранее, сколько строк мне нужно.
У кого-нибудь есть решение или хотя бы подсказка?
Редактировать: Спасибо AlexCuse за решение, который работает в моем конкретном случае. Теперь перейдем к более важному вопросу: как заставить работать RAND?


Если в вашей таблице есть столбец (возможно, даже столбец рядовой), который является числовым в общем смысле, например целочисленным, с плавающей запятой или числовым SQL, попробуйте следующее:
SELECT * FROM SomeTable WHERE SomeColumn='SomeCondition' AND 0*rowid+RAND() < 0.10
Чтобы оценить RAND() один раз для каждая строка, а не один раз в начало вашего запроса.
Виноват оптимизатор запросов. Возможно, есть другой способ, но я верю, что он сработает для вас.
Если это оптимизатор, то это очень похоже на мультик!
Возможно, это моя вина, но говоря «числовой», я имел в виду любой числовой тип (целое, краткое, с плавающей запятой, числовой «SQL» и т. д.). Пожалуйста, проверьте мой ответ еще раз.
Такой подход (обозначенный знаком ΤΖΩΤΖΙΟΥ) не гарантирует 10% выборки. Он предоставит вам только все строки, в которых Rand () оценивается как <0,10, что не будет согласованным.
Что-то типа
select top 10 percent * from MyTable order by NEWID()
сделает свое дело.
редактировать: на самом деле нет хорошего способа заставить работать RAND. Это то, что я использовал в прошлом (предупреждение kludge - это убивает вас, когда вы не можете использовать Rand () в UDF)
CREATE VIEW RandView AS
SELECT RAND() AS Val
GO
CREATE FUNCTION RandomFloat()
RETURNS FLOAT
AS
BEGIN
RETURN (SELECT Val FROM RandView)
END
Тогда у вас просто select blah, dbo.RandomFloat() from table в вашем запросе.
Мне было достаточно приближения к 10%, но ваш ответ прекрасно решает мою непосредственную проблему. Я должен был подумать проверить предложение PERCENT в TOP.
Вы видели этот вопрос?
Как мне вернуть случайные числа в виде столбца в SQL Server 2005?
Адам опубликовал UDF, который можно использовать вместо Rand (), который работает намного лучше.
Кажется, это работает:
select * from SomeTable
where rand(0*SomeTableID + cast(cast(newid() as binary(4)) as int)) <= 0.10
Кажется, это работает
SELECT TOP 10 PERCENT * FROM schema.MyTable ORDER BY NEWID()
Этот метод у меня не работает. У меня есть только varchar и int, и я не понимаю, почему int будет вести себя иначе, чем numeric.