Вибираємо випадковий запис із таблиці MySQL
Нещодавно зіткнувся з такою проблемкою, вирішив поділитися. В документації MySQL наведено приклад запиту для вибірки випадкового запису.
SELECT * FROM tbl_name ORDER BY RAND() LIMIT 0, 1
Експеримент показав, що такий запит на табличці з > 500000 записів зайняв 27.5 сек. :( Що ж робити, як нам бути?
Варто сказати, що така повільна робота зумовлена тим, що MySQL створює тимчасову таблицю, де кожному рядку з оригінальної ставить у відповідність випадкове значення, створюючи при цьому індекс для сортування. Далі рядки сортуються та повертаються.
Це можна та потрібно обходити. У цьому нам допоможе інструкція LIMIT. Як ми всі пам’ятаємо, LIMIT задає початковий порядковий номер записів у результаті та об’єм вибірки у вигляді LIMIT <start>, <count>. Таким чином, забезпечивши випадкове значення параметра LIMIT, можна досягнути бажаного результату.
На жаль, в MySQL неможливо задати вираз з SELECT для параметра LIMIT. А тому нам доведеться використати два запити: перший визначить випадковий порядковий номер, а другий здійснить вибірку потрібного рядка. Отже, перший із них:
SELECT FLOOR(COUNT(*) * RAND()) FROM tbl_name
Зберігаємо отримане значення, наприклад, у змінній offset та викликаємо наступний запит:
SELECT * FROM tbl_name LIMIT <offset>, 1
Якщо вам не подобається виклик двох послідовних запитів, то в принципі є ще один варінт. Вирази із SELECT можна вставляти в конструкції WHERE. Таким чином, можна писати запити на зразок наступного:
SELECT * FROM tbl_name t1 WHERE t1.id >= (SELECT FLOOR(MAX(t2.id) * RAND()) FROM tbl_name t2) LIMIT 0, 1
Однак при подібних запитах дуже важко гарантувати вибірку з рівномірним розподілом, оскільки ідентифікатори з вашим первинним ключем зовсім необов’язково йтимуть по порядку. Дуже яскраво це видно, коли потрібно вибирати запис запитом, що містить INNER JOIN. Це суттєво впливає на розподіл ідентифікаторів. Словом, такий варіант можна використовувати, лише коли твердо знаєте, яким законам підкорюється життя записів у ваших таблицях.
Останній варіант запиту викликав невдоволення у Павла Тайкало, оскільки при вибірці ідентифікаторів ми не можемо гарантувати їхній порядок, а тому є небезпека зіткнутися з виродженими результатами, вибираючи один і той самий запис кожного разу. Я згоден з Павлом, однак на практиці останній варіант таки працює, от тільки використовувати його я вам не рекомендую, оскільки на таблиці з 32000 записами він забирав близько 4 сек, а для 500000 рядків я не дочекався результату протягом 2 хвилин. :)






Пока нет комментариев.