Випадковий запис із таблиці бази даних (T-SQL)


85

Чи є стислий спосіб отримати випадковий запис із таблиці SQL-сервера?

Я хотів би рандомізувати свої дані модульного тесту, тому шукаю простий спосіб вибрати випадковий ідентифікатор із таблиці. Англійською мовою буде вибрано "Виберіть один ідентифікатор із таблиці, де ідентифікатором є випадкове число між найнижчим ідентифікатором у таблиці та найвищим ідентифікатором у таблиці".

Я не можу зрозуміти, як це зробити, без необхідності запускати запит, перевіряти наявність нульового значення, а потім повторно запускати, якщо значення нульове.

Ідеї?


є тут кілька методів brettb.com/SQL_Help_Random_Numbers.asp
Mesh

2
Ви впевнені, що хочете застосувати такий підхід? Дані одиничного тесту не повинні бути випадковими - насправді, вам слід гарантувати отримання однакових результатів незалежно від того, скільки разів ви виконували одиничний тест. Наявність випадкових даних може порушити цей фундаментальний принцип модульного тестування.
поводи

Посилання вище від @Mesh більше не активне.
Роберт Сіверс,

Відповіді:


145

Чи є стислий спосіб отримати випадковий запис із таблиці SQL-сервера?

Так

SELECT TOP 1 * FROM table ORDER BY NEWID()

Пояснення

NEWID()Для кожного рядка генерується A , після чого таблиця сортується за ним. Повертається перший запис (тобто запис із "найнижчим" GUID).

Примітки

  1. GUID генеруються як псевдовипадкові числа з четвертої версії:

    UUID версії 4 призначений для генерації UUID з справді випадкових чи псевдовипадкових чисел.

    Алгоритм такий:

    • Встановіть два найбільш значущі біти (біти 6 і 7) clock_seq_hi_a__reserved рівними нулю та одиниці відповідно.
    • Встановіть для чотирьох найбільш значущих бітів (біти з 12 по 15) поля time_hi_and_version 4-бітний номер версії з розділу 4.1.3.
    • Встановіть для всіх інших бітів випадкові (або псевдовипадкові) вибрані значення.

    - Універсальний унікальний ідентифікатор (UUID) URN Простір імен - RFC 4122

  2. Альтернатива SELECT TOP 1 * FROM table ORDER BY RAND()не буде працювати, як можна подумати. RAND()повертає одне єдине значення на запит, отже всі рядки матимуть одне і те ж значення.

  3. Хоча значення GUID є псевдовипадковими, вам знадобиться кращий PRNG для більш вимогливих програм.

  4. Типова продуктивність становить менше 10 секунд для приблизно 1 000 000 рядків - звичайно, залежно від системи. Зверніть увагу, що неможливо досягти індексу, тому продуктивність буде відносно обмеженою.


Саме те, що я шукав. У мене було відчуття, що це простіше, ніж я робив це.
Джеремі

1
Ви припускаєте, що NEWID видає псевдовипадкові значення. Є велика ймовірність, що це призведе до послідовних значень. NEWID просто створює унікальні цінності. RAND, однак, створює псевдовипадкові значення.
Skizz

Я запускаю його на сильно індексованій таблиці з 1 671 145 рядками, і повернення займає 7 секунд. Таблиця теж досить оптимальна - це практично серце нашої бази даних, тому про неї подбали.
Том Ріттер,

@ ÂviewAnew. 1,6 мільйона рядків і 7 секунд на вибірці, яка не (і не може) потрапити в індекс, непогана.
Скліввз

7
@Skizz, rand не працює так. ОДНІ випадкове значення генерується перед SELECT. Тож якщо ви спробуєте "ВИБЕРИТИ ТОП 10 РАНДУ () ...", ви завжди отримаєте одне і те ж значення
Sklivvz

27

На більших таблицях ви також можете використовувати TABLESAMPLEце, щоб уникнути сканування цілої таблиці.

SELECT  TOP 1 *
FROM YourTable
TABLESAMPLE (1000 ROWS)
ORDER BY NEWID()

Це ORDER BY NEWIDвсе ще потрібно, щоб уникнути просто повернення рядків, які відображаються першими на сторінці даних.

Число, яке потрібно використовувати, слід ретельно вибирати відповідно до розміру та визначення таблиці, і ви можете розглянути можливість повторної логіки, якщо не повертається жоден рядок. Тут обговорюється математика, яка лежить в основі цього, і чому методика не підходить для невеликих таблиць


Я знайшов це на веб-сайті корпорації Майкрософт: Ви можете використовувати TABLESAMPLE для швидкого повернення зразка з великої таблиці, коли виконується одна з наступних умов: Зразок не повинен бути справді випадковою вибіркою на рівні окремих рядків. Рядки на окремих сторінках таблиці не співвідносяться з іншими рядками на одній сторінці.
Марк Ентінг

1
@MarkEntingh - У цьому випадку TOP 1не має значення, чи співвідносяться рядки на одній сторінці чи ні. Ви вибираєте лише одну з них.
Мартін Сміт,

9

Також спробуйте свій метод, щоб отримати випадковий ідентифікатор між MIN (Id) та MAX (Id), а потім

SELECT TOP 1 * FROM table WHERE Id >= @yourrandomid

Ви завжди отримаєте один ряд.


2
-1, це спрацює лише тоді, коли між мінімальним та максимальним значеннями відсутні відсутні ідентифікатори Якщо один видалено, тоді той самий ідентифікатор генерується випадковою функцією, ви отримаєте назад нуль записів.
Ніл Н,

6
@Neil, не дуже - це дасть вам перший рядок з ідентифікатором, більшим ніж випадкове число, якщо відсутні ідентифікатори. Проблема тут полягає в тому, що ймовірність виходу кожного рядка не є постійною. Але знову ж цього достатньо в більшості випадків.
Sklivvz

1
+1. Для модульного тестування, яке має досягти різних значень, досить добре - якщо вам потрібен справжній випадковий результат, то це щось інше. Але в контексті ОП це має бути досить добре.
TomTom

7

Якщо ви хочете вибрати великі дані, найкращим способом, який я знаю, є:

SELECT * FROM Table1
WHERE (ABS(CAST(
    (BINARY_CHECKSUM
    (keycol1, NEWID())) as int))
    % 100) < 10

Джерело: MSDN


Я не впевнений, але я думаю, що використання RAND (), а NEWID () для генерування справді випадкових чисел може бути кращим через недоліки використання NEWID () в обраному процесі.
QMaster

Я намагаюся використовувати цей метод із точною кількістю записів, а не з відсотковою базою, я зробив це з розширенням діапазону вибору та обмеженням за допомогою TOP n, чи є пропозиція?
QMaster

Я знайшов ще одну проблему з цим сценарієм. Якщо ви використовуєте групу, ви завжди отримуватимете однаковий порядок випадково вибраних рядків, тому, здається, у невеликих таблицях підхід @skilvvz є найбільш правильним.
QMaster

0

Я прагнув вдосконалити методи, які випробував і натрапив на цю публікацію. Я розумію, що він старий, але цього методу немає в списку. Я створюю та застосовую тестові дані; це показує метод "адреси" в ІП, що викликається з @st (два символи)

Create Table ##TmpAddress (id Int Identity(1,1), street VarChar(50), city VarChar(50), st VarChar(2), zip VarChar(5))
Insert Into ##TmpAddress(street, city, st, zip)
Select street, city, st, zip 
From tbl_Address (NOLOCK)
Where st = @st


-- unseeded RAND() will return the same number when called in rapid succession so
-- here, I seed it with a guaranteed different number each time. @@ROWCOUNT is the count from the most recent table operation.

Set @csr = Ceiling(RAND(convert(varbinary, newid())) * @@ROWCOUNT)

Select street, city, st, Right(('00000' + ltrim(zip)),5) As zip
From ##tmpAddress (NOLOCK)
Where id = @csr

0

Якщо ви дійсно хочете випадкову вибірку окремих рядків, змініть свій запит, щоб випадково відфільтрувати рядки, замість того, щоб використовувати TABLESAMPLE. Наприклад, наступний запит використовує функцію NEWID для повернення приблизно одного відсотка рядків таблиці Sales.SalesOrderDetail:

SELECT * FROM Sales.SalesOrderDetail
WHERE 0.01 >= CAST(CHECKSUM(NEWID(), SalesOrderID) & 0x7fffffff AS float)
/ CAST (0x7fffffff AS int)

Стовпець SalesOrderID включено у вираз CHECKSUM, щоб NEWID () обчислював один раз на рядок для досягнення вибірки на основі кожного рядка. Вираз CAST (CHECKSUM (NEWID (), SalesOrderID) & 0x7fffffff AS float / CAST (0x7fffffff AS int) обчислюється випадковим значенням плаваючого значення від 0 до 1. "

Джерело: http://technet.microsoft.com/en-us/library/ms189108(v=sql.105).aspx

Це додатково пояснюється нижче:

Як це працює? Давайте розділимо речення WHERE і пояснимо це.

Функція CHECKSUM обчислює контрольну суму над елементами у списку. Можна сперечатися щодо того, чи взагалі потрібен SalesOrderID, оскільки NEWID () - це функція, яка повертає новий випадковий GUID, тому множення випадкової цифри на константу в будь-якому випадку має призвести до випадкового. Дійсно, виключення SalesOrderID, здається, не має ніякої різниці. Якщо ви захоплений статистик і можете виправдати включення цього, скористайтесь розділом коментарів нижче та повідомте мені, чому я помиляюся!

Функція CHECKSUM повертає VARBINARY. Виконання побітової операції І з 0x7fffffff, що є еквівалентом (111111111 ...) у двійковому вигляді, дає десяткове значення, яке фактично є поданням випадкового рядка 0 і 1. Поділ на коефіцієнт 0x7fffffff ефективно нормалізує цю десяткову цифру до цифри від 0 до 1. Потім, щоб вирішити, чи заслуговує кожен рядок включення в кінцевий набір результатів, використовується поріг 1 / x (у даному випадку 0,01), де x - відсоток даних, які потрібно отримати як зразок.

Джерело: https://www.mssqltips.com/sqlservertip/3157/different-ways-to-get-random-data-for-sql-server-data-sampling

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.