Як взяти ефективну просту випадкову вибірку в SQL? У цій базі даних працює MySQL; моя таблиця складає щонайменше 200 000 рядків, і я хочу просту випадкову вибірку приблизно 10 000.
"Очевидна" відповідь:
SELECT * FROM table ORDER BY RAND() LIMIT 10000
Для великих таблиць це занадто повільно: він викликає RAND()
кожен рядок (що вже ставить його як O (n)) і сортує їх, роблячи в кращому випадку O (n lg n). Чи є спосіб зробити це швидше, ніж O (n)?
Примітка : Як зазначає Ендрю Мао в коментарях, якщо ви використовуєте цей підхід на SQL Server, вам слід використовувати функцію T-SQL NEWID()
, оскільки RAND () може повертати одне і те ж значення для всіх рядків .
РЕДАГУВАТИ: ПІЗНІШЕ 5 РОКІВ
Я знову зіткнувся з цією проблемою з більшою таблицею і в кінцевому підсумку використав версію рішення @ ignorant із двома налаштуваннями:
- Зрабіть зразки рядків у 2-5 разів за моїм бажаним розміром вибірки, щоб дешево
ORDER BY RAND()
- Зберігайте результат
RAND()
в індексованому стовпці при кожному вставці / оновленні. (Якщо ваш набір даних не дуже важкий для оновлення, можливо, вам доведеться знайти інший спосіб зберегти цей стовпець свіжим.)
Щоб взяти зразок таблиці з 1000 елементів, я підраховую рядки і вибираю результат до, в середньому, 10000 рядків із стовпцем frozen_rand:
SELECT COUNT(*) FROM table; -- Use this to determine rand_low and rand_high
SELECT *
FROM table
WHERE frozen_rand BETWEEN %(rand_low)s AND %(rand_high)s
ORDER BY RAND() LIMIT 1000
(Моя фактична реалізація передбачає більше роботи, щоб переконатись, що я не недоозброюю, і вручну обернути rand_high, але основна ідея - "випадковим чином скоротити ваш N до кількох тисяч.")
Незважаючи на те, що це приносить деякі жертви, це дозволяє мені взяти вибірку бази даних за допомогою сканування індексу, поки вона не стане достатньо маленькою, щоб ORDER BY RAND()
знову.
RAND()
повертає одне і те ж значення при кожному наступному виклику.