У меня есть две таблицы. В одной таблице id пользователей, в другой id пользователей, которых функция рандома не должна вызвать. Надо вывести 3 рандомных не повторяющихся числа из переменной $allowed.
$banned = "SELECT id FROM admin";
$allowed = "SELECT id FROM 10a WHERE id NOT IN (". $banned .")";
Простейший вариант, который судя по названию таблицы 10a (школа?), Вам подойдёт:
SELECT id
FROM 10a
WHERE id NOT IN (SELECT id FROM admin)
ORDER BY RAND()
LIMIT 3
Недостаток этого варианта - логарифмическое замедление при увеличении количества строк в таблице 10a, так как ORDER BY RAND() вызывает сканирование и затем сортировку всей таблицы целиком - и без использования индексов.
Вот здесь и здесь всё это неплохо расписано.
Вариант для больших таблиц, который мне кажется предпочтительным, это вставка в таблицу дополнительного индексированного float столбца rnd, в который при создании строки записывается результат функции RAND(). В этом случае рандомизация выполняется на этапе заполнения таблицы, а при выборке мы случайным образом определяем начальное значение столбца rnd и выполняем чтение нужного количества строк, отсортированных по rnd.
Пример на db-fiddle. Схемы таблиц и запрос - ниже:
CREATE TABLE 10a (
id INT NOT NULL AUTO_INCREMENT,
name text,
rnd float,
PRIMARY KEY (id)
);
CREATE TABLE admin (
id INT
);
CREATE TRIGGER 10a_bi
BEFORE INSERT ON 10a
FOR EACH ROW
SET NEW.rnd = RAND();
CREATE INDEX rnd_idx ON 10a(rnd);
Заметьте, что в запросе 2 раза встречается число 3 - количество возвращаемых значений. Это сделано для того, чтобы "неудачники" с 3 самыми большими значениями rnd имели равновероятный шанс попасть в выдачу, как и все остальные
SELECT r.*
FROM
(
SELECT RAND() *
(
SELECT rnd
FROM 10a
WHERE id NOT IN (SELECT id FROM admin)
ORDER BY rnd DESC
LIMIT 1 OFFSET 3
) start
) init
JOIN 10a r
WHERE r.rnd > init.start
AND id NOT IN (SELECT id FROM admin)
ORDER BY r.rnd
LIMIT 3;
Продвижение своими сайтами как стратегия роста и независимости