На SQL:
Я думаю, что для ваших целей сгодится псевдослучайная перестановка.
Шаг 1: Сохраните длину таблицы, пусть $N.
Шаг 2: Выбирите большое (больше $N) число $P, взаимопростое с $N. Это можно сделать с помощью алгоритма для генерации простых чисел.
Шаг 3: Пусть (Q) это запрос, который возвращает нужные вам данные из таблицы, а также поле rownum, являющееся номером строки соответствующей записи. Тогда теперь вы можете сделать так:
SELECT * FROM (Q) ORDER BY $P * rownum % $N LIMIT $limit, 10;
Не на SQL:
Шаг 1:
Сгенерируйте случайную перестановку заранее, пусть она будет в массиве $perm, сохраните ее в сессию или еще куда-то.
Шаг 2: Выберите нужные индексы:
$indices = array_slice($perm, $limit, 10);
$indices = join(", ", $indices);
Шаг 3: Запрос: SELECT * FROM (Q) WHERE rownum IN ($indices)
Если хочется без подзапроса, дающего номера строк, то придется запоминать первичные ключи уже возвращенных записей и добавлять в конец запроса WHERE id_key NOT IN (...).
UPD Еще подход, попроще.
Запрос такой:
"SELECT * FROM table ORDER BY func(id_key) LIMIT $limit, 10;"
Здесь func это некоторая функция, преобразующая первичный ключ случайным обзром (он ведь числовой?). Например, подойдет $P * id_key % $T, где $P это большое случайное нечетное число, сгенерированное заранее, а $T это степень двойки большая последнего id_key.
Например, подойдет
$T = 2^31;
$P = (rand(2^26, 2^30) - 1) * 2 - 1;
Запрос будет такой:
"SELECT * FROM thetable ORDER BY CAST(id_key AS BIGINT) * $P % $T LIMIT $limit, 10;"
Это даст около миллиарда различных вариантов перемешивания.
В случае, если в таблицу будет добавлена новая запись, или удалена старая, все эти варианты (кроме запоминания уже вышедших айдишников) могут дать сбой, а именно попадание одной старой записи в выборку еще раз.