- Быстрый выбор случайных значений из больших таблиц MySQL по условию
- Заполнение таблицы через array_rand
- Заполнить массив случайными числами (PHP)
- Функция array_fill_rand()
- Категории
- Читайте также
- Комментарии
- Вход на сайт
- Социальные сети
- Как создать таблицу на 1М записей одним запросом
- Случайные числа
- Числовая последовательность
- Рандомизированные данные
Быстрый выбор случайных значений из больших таблиц MySQL по условию
Задача выбора случайных строчек из таблицы довольно часто возникает перед разработчиками.
В случае, если используется СУБД MySQL, обычно она решается примерно следующим способом:
SELECT *
FROM users
WHERE role_id=5
ORDER BY rand()
LIMIT 10
Такой код работает крайне медленно для больших таблиц.
Если в запросе не нужно использовать WHERE или таблица небольшая, есть эффективные решения, например habrahabr.ru/post/54176 или habrahabr.ru/post/55864.
Но готовых решений для большой таблицы и необходимости фильтровать по условию, получая при каждом запросе новые значения, я не нашел, поэтому описание моего способа под катом.
Как оказалось, MySQL не умеет эффективно выбирать случайные строки с помощью ORDER BY rand() LIMIT N, где необходимо отфильтровать строки по условию (хотя тот же MSSQL отлично справляется с выбором случайных строк из таблицы с большим количеством записей).
Итак, решая задачу «в лоб», запрос (в таблице 5млн. записей):
SELECT *
FROM users
WHERE role_id=5
ORDER BY rand()
LIMIT 10
Запрос занял 41.3544 сек., что недопустимо долго. Найти максимальный и минимальный id, а затем выбрать случайные id из промежутка в данном случае нельзя: из-за условия WHERE, id идут уже не по порядку и разряженно.
Мое решение следующее: добавляется таблица random_seed, содержащая поля id и random_seed, заполняемая случайными числами, на данную колонку добавляется индекс, также индекс добавляется на колонку, по которой будет происходить выборка.
Теперь, чтобы выбрать случайные строки по условию, запрос нужно поменять следующим образом (в таблице 5млн. записей):
SELECT
u1.*
FROM
users u1,
random_seed rs
WHERE
u1.role_id=5 AND u1.id=(rs.id+random_from_php)
ORDER BY
rs.random_seed
LIMIT 10
Запрос занял 0.0460 сек., что является уже более чем приемлемым результатом. Переменная random_from_php генерируется вызывающим запрос кодом, что обеспечивает случайный набор значений при каждом запросе, это число обеспечит выборку по новым случайным числам. В таблице random_seed должно быть столько же значений, сколько в таблице, из которой нужно брать случайные строки + N записей, где N это максимально возможное значение random_from_php.
Реальный пример из моей работы — выбор случайных категорий товаров из разных комбинаций (всего 4000000 записей):
«Обычный»: запрос | «Ускоренный» запрос: |
SELECT oc1.* FROM object_category oc1 WHERE oc1.region_id=6 ORDER BY RAND() LIMIT 10 | SELECT oc1.* FROM object_category oc1, random_seed rs WHERE oc1.id=(rs.id+564756) AND oc1.region_id=6 ORDER BY rs.random_seed LIMIT 10 |
Время выполнения: | |
1.726с | 0.007с |
1.851с | 0.010с |
1.803с | 0.006с |
1.784с | 0.008с |
Преимущества описанного выше метода:
+ Самый быстрый из возможных способов выбора случайных строк из таблицы по условию
+ Не нужно повторно генерировать случайные числа для каждой из строк в таблице
+ Запрос всех нужных значений происходит не итерационно, в один запрос
Минусы:
— Необходимость ввода дополнительной таблицы
— Необходимость изменения привычных запросов
Заполнение таблицы через array_rand
Доброго времени суток.
Сделал кнопку, которая генерит таблицу с заданными значениями, теперь вот застрял на моменте, когда нужно заполнить ячейки созданной таблицы рандомными элементами массива. Вопроса два:
1) Что значит число 2 в этой строчке?
$rand_keys=array_rand($array1, 2);
Количество забираемых элементов из массива, как я понял. В моем случае с таблицей оно должно быть равно общему числу ячеек этой таблицы? Или я что-то путаю?
2) Как нужно модифицировать
$table .= "X $i Y $j";//тут закидываю в ячейки их координаты, дальше должно пригодиться. $table .= " "; $j++;
так, что бы сюда выводились случайные значения из массива? Подскажите пожалуйста. Весь код под спойлером.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42
header('Content-Type: text/html; charset=utf-8'); ?> $array1 = array("1", "2", "3", "4", "5", "6", "7", "8"); $rand_keys = array_rand($array1, 2); echo $array1[$rand_keys[0]] . "\n";//проверка работоспособности функции function draw_table($row,$col) { // Функция для вывода таблицы $table = ""; $i = 1; do { $table .= ""; $j = 1; do { $table .= "X $i Y $j"; $table .= " "; $j++; } while($j $col); $table .= " "; $i++; } while($i $row); $table .= "
"; return $table; } if(isset($_POST['zero'])) { $rows = $_POST['row']; // количество строк, tr $cols = $_POST['col']; // количество столбцов, td echo(draw_table($rows, $cols)); } ?>
array_rand()
функция раздает 6 случайных карт из колоды. Используя функцию, раздать карты трем игрокам. Карты у.
Не могу понять принцип array_rand
Здравствуйте. Стоит задача вывести рандомом одно случайное значение из массива. Использую.
Из за чего может не правильно работать array_rand ?
Почему у меня он возвращает либо пустое значение, либо имя переменной($arr)? Использую сессии.
Не отображается array_rand, что делаю не так?
Зря создал тему, можно удалять. Нашел решение своей проблемы. Просто нужно было полазить по.
$arr = [1,2,3,4,5]; $arr1 = array_rand($arr,2); echo ''; echo '
';'; foreach($arr1 as $i){ echo ' '; echo ''.$i.' '; } echo '
Нужно заполнить все ячейки созданной таблицы рандомными элементами массива.
Я не могу понять, как совместить эту функцию и свою строчку с
Сообщение от TwinSpike
Сама функция более менее понятна, берется массив arr с определенными элементами, потом берется $arr1, в котором два рандомных элемента из первого массива, потом через foreach каждый элемент выводится через эхо. Наверно. :С
Суть я смутно понимаю, но совместить это со своим кодом не получается.
Добавлено через 3 часа 30 минут
После долгих мучений удалось привести код к такому виду:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34
header('Content-Type: text/html; charset=utf-8'); ?> function draw_table($row,$col) { $array1 = array("машина", "огонь", "пустота", "голова", "магия", "вещество", "потребление", "глупость"); echo ''."\n"; for($r = 1; $r $row; ++$r) { echo ''."\n"; for($c = 1; $c $col; ++$c) { $rand_keys = array_rand($array1, 2); echo ''.$array1[$rand_keys[0]].' '; } echo ' '."\n"; } echo '
'; } if(isset($_POST['zero'])) { $rows = $_POST['row']; $cols = $_POST['col']; draw_table($rows,$cols); } ?>
Заполнить массив случайными числами (PHP)
В PHP нет стандартной функции для генерации массива случайными числами. Самый простой способ написать функцию array array_fill_rand(int limit, [ min, max]) , которая принимает в качестве параметров: int limit — количество элементов массива и два необязательных параметра int min и int max — минимальное и максимальное значение элемента массива.
Функция array_fill_rand()
Напишем функцию array_fill_rand() и приведем пример использования.
> else < for ($i=0; $i> return $array; > echo ‘
'; // Массив из 5 элементов $rand_array = array_fill_rand(5); print_r($rand_array); // Массив из 10 элементов $rand_array = array_fill_rand(10); print_r($rand_array); // Массив из 5 элементов, со случайными числами в диапазоне от 0 до 10 $rand_array = array_fill_rand(5, 0, 10); print_r($rand_array); // Массив из 10 элементов, со случайными числами в диапазоне от -100 до 100 $rand_array = array_fill_rand(10, -100, 100); print_r($rand_array); echo '
‘; ?>?php>
В результате на экране появится 4 массива заполненные случайными числами, например:
Array ( [0] => 12563 [1] => 24400 [2] => 9545 [3] => 20046 [4] => 19311 ) Array ( [0] => 8316 [1] => 19717 [2] => 346 [3] => 12171 [4] => 18536 [5] => 13441 [6] => 22822 [7] => 14695 [8] => 788 [9] => 6077 ) Array ( [0] => 8 [1] => 0 [2] => 5 [3] => 9 [4] => 8 ) Array ( [0] => 73 [1] => -49 [2] => -98 [3] => 83 [4] => -73 [5] => -57 [6] => 0 [7] => 37 [8] => 16 [9] => 40 )
Категории
Читайте также
- Заполнить массив случайными числами (JavaScript)
- Умножить массив на число (PHP)
- Преобразовать массив в объект (PHP)
- Цикл в обратном порядке (PHP)
- Массив уникальных значений (JavaScript)
- Ассоциативный массив в JavaScript
- Как записать массив в файл (PHP)
- Получить последнее значение массива (PHP)
- Получить первое значение массива (PHP)
- Массив в строку (JavaScript)
- Получить массив ключей (PHP)
- Сортировать числовой массив (JavaScript)
Комментарии
Автору статьи ставлю Like!.
Описано все предельно ясно; без «лишней воды» так сказать. Несмотря на то что я начал изучать php совсем недавно все что описано выше мне понятно и функцию эту я нахожу очень полезной и простой в понимании.
Спасибо что выложил статью и тем самым поделился опытом. Так что мне теперь понятно как реализовать рандомное заполнение значений элементов массива, да еще и с ограничением допустимых мин и мах значений.
Вход на сайт
Введите данные указанные при регистрации:
Социальные сети
Вы можете быстро войти через социальные сети:
Как создать таблицу на 1М записей одним запросом
Допустим, вы хотите проверить, как поведет себя запрос на большой таблице — но такой таблицы под рукой нет. Если СУБД умеет в рекурсию, это не проблема: кучу данных можно нагенерить одним запросом. Поможет в этом конструкция WITH RECURSIVE .
Я буду использовать SQLite, но похожие запросы сработают в PostgreSQL и других СУБД. WITH RECURSIVE поддерживается в MariaDB 10.2+, MySQL 8.0+, PostgreSQL 8.4+ и SQLite 3.8+. Oracle 11.2+ и SQL Server 2005+ поддерживают рекурсивные запросы, но без ключевого слова RECURSIVE .
Случайные числа
Создадим таблицу на 1 млн случайных чисел:
create table random_data as with recursive tmp(x) as ( select random() union all select random() from tmp limit 1000000 ) select * from tmp;
Или, если ваша база поддерживает generate_series() (и не поддерживает limit в рекурсивных запросах, как PostgreSQL):
create table random_data as select random() as x from generate_series(1, 1000000);
sqlite> select count(*) from random_data; 1000000 sqlite> select avg(x) from random_data; 1.000501737529e+16
Числовая последовательность
Вместо случайных чисел заполним таблицу числами от единицы до миллиона:
create table seq_data as with recursive tmp(x) as ( select 1 union all select x+1 from tmp limit 1000000 ) select * from tmp;
Или через generate_series() :
create table seq_data as select value as x from generate_series(1, 1000000);
sqlite> select count(*) from seq_data; 1000000 sqlite> select avg(x) from seq_data; 500000.5 sqlite> select min(x) from seq_data; 1 sqlite> select max(x) from seq_data; 1000000
Рандомизированные данные
Числа — это хорошо, но что, если нужна большая табличка с данными о клиентах? Запросто!
- у клиента есть идентификатор, имя и возраст;
- идентификатор заполняем последовательно от 1 до 1000000;
- имя случайным образом выбираем из фиксированного списка;
- возраст берем случайный от 1 до 80.
Создадим таблицу с именами:
create table names ( id integer primary key, name text ); insert into names(id, name) values (1, 'Анна'), (2, 'Борис'), (3, 'Вера'), (4, 'Галина'), (5, 'Денис');
create table person_data as with recursive tmp(id, idx, name, age) as ( select 1, 1, 'Анна', 20 union all select tmp.id + 1 as id, abs(random() % 5) + 1 as idx, (select name from names where as name, abs(random() % 80) + 1 as age from tmp limit 1000000 ) select id, name, age from tmp;
Или через generate_series() :
create table person_data as with tmp as ( select value as id, abs(random() % 5) + 1 as idx, abs(random() % 80) + 1 as age from generate_series(1, 1000000) ) select id, (select name from names where as name, age from tmp;
- идентификатор рассчитывается как предыдущее значение + 1;
- поле idx содержит случайное число от 1 до 5;
- имя выбирается из таблицы names по значению idx ;
- возраст рассчитывается как случайное число от 1 до 80.
sqlite> select count(*) from person_data; 1000000 sqlite> select * from person_data limit 10; ┌────┬───────┬─────┐ │ id │ name │ age │ ├────┼───────┼─────┤ │ 1 │ Анна │ 20 │ │ 2 │ Анна │ 76 │ │ 3 │ Борис │ 25 │ │ 4 │ Борис │ 19 │ │ 5 │ Борис │ 11 │ │ 6 │ Вера │ 72 │ │ 7 │ Анна │ 41 │ │ 8 │ Денис │ 9 │ │ 9 │ Денис │ 38 │ │ 10 │ Вера │ 41 │ └────┴───────┴─────┘
Миллион клиентов одним запросом, неплохо! Вот бы в продажах так ツ
Если хотите узнать больше о рекурсивных SQL-запросах, прикладном анализе данных и SQLite — записывайтесь на курс:
Курс расскажет, как использовать SQLite для повседневной работы с данными. Без воды, куча примеров, применяйте в работе с первого дня.
Подписывайтесь на канал, чтобы не пропустить новые заметки 🚀