Случайный ряд из таблицы MySql, сейчас пол секунды. А максимально быстро как?

В таблице пол миллиона записей. Нужно случайно выбирать одну для заполнения другой, тестовой таблицы.
Сейчас уходит пол секунды на получение случайной строки... Это очень долго,.
Пробую так:
$loc = DB::table("localities")
                ->select("id")
                ->orderBy(DB::raw('RAND()'))
                ->take(1)
                ->get();

Это равносильно:
0 => array:3 [▼
    "query" => "select `id` from `localities` order by RAND() asc limit 1"
    "bindings" => []
    "time" => 95.69
  ]

Пол секунды:
И так пол секунды:
$loc = Locality::inRandomOrder()->first();
Это равносильно:
0 => array:3 [▼
    "query" => "select * from `localities` order by RAND() limit 1"
    "bindings" => []
    "time" => 432.94
  ]


И это все долго, индексы на id таблицы стоят по умолчанию:
Неуникальный: 0
Имя ключа: PRIMARY
Порядок поля: 1
Имя колонки: id
Сравнение: A
Размер: 186060
Обработка: NULL
Сжатие: NULL
  • Вопрос задан
  • 1258 просмотров
Решения вопроса 1
iiifx
@iiifx
PHP, OOP, SOLID, Yii2, Composer, PHPStorm
1. Определите кол-во записей в таблице. Не последний ID, а именно кол-во
2. Определите рандомное значение между 0 и кол-вом-1 записей, это будет смещение
3. Получите нужную запись с таблицы:
select `id` from `localities` offset {$offset} limit 1


Не сравнивал скорость, но должно быть в разы быстрее.
Рандомное смещение можно получить как на стороне PHP, так и на стороне MySQL.
А о RAND() вам уже написали.

Детально:
У вас N записей в таблице, вам нужно получить через mt_rand(), к примеру, случайное число от 0 до N-1. Это и будет ваше смещение.
$count = 100;
$offset = mt_rand( 0, $count-1 );
Ответ написан
Пригласить эксперта
Ответы на вопрос 2
qonand
@qonand
Software Engineer
RAND() сам по себе тяжелая операции, которую лучше не использовать. В интернете (в том числе и на хабре) много статей посвященных оптимизации ORDER BY RAND(), вот например одна из них
Ответ написан
Комментировать
Rsa97
@Rsa97
Для правильного вопроса надо знать половину ответа
ORDER BY RAND() не индексируется в принципе
Можно попробовать
SET @rand = (SELECT (MAX(`id`)-MIN(`id`))*RAND()+MIN(`id`) FROM `localities``);
SELECT * FROM `localities` WHERE WHERE `id` > @rand LIMIT 1;

Но распределение будет неравномерным, в местах, где есть пропуски `id` следующие за ними строки будут выпадать с большей вероятностью.
Ответ написан
Комментировать
Ваш ответ на вопрос

Войдите, чтобы написать ответ

Похожие вопросы