2014-01-17 2 views
2

Я пытаюсь найти правильный способ получения X случайных записей. Также я хочу проверить использование конкретной записи, поэтому я не буду использовать ту же случайную запись, что и другие.Получение минимальных случайных записей через MySQL

Я тестирую набор с этими тремя таблицами, одной таблицей для вопросов, одной таблицей для пользователей и одной таблицей для обслуживаемого вопроса для конкретного пользователя. Я хочу сделать это с примерно 6000 вопросами.

CREATE TABLE `questions` (
    `id` int(11) NOT NULL AUTO_INCREMENT, 
    `question` varchar(128) DEFAULT NULL, 
    PRIMARY KEY (`id`) 
) ENGINE=InnoDB DEFAULT CHARSET=utf8; 


CREATE TABLE `served` (
    `user` int(11) NOT NULL DEFAULT '0', 
    `question` int(11) NOT NULL DEFAULT '0', 
    `count` varchar(128) DEFAULT NULL, 
    PRIMARY KEY (`user`,`question`), 
    KEY `count` (`count`) USING BTREE 
) ENGINE=InnoDB DEFAULT CHARSET=utf8; 


CREATE TABLE `users` (
    `id` int(11) NOT NULL AUTO_INCREMENT, 
    `nickname` varchar(128) DEFAULT NULL, 
    PRIMARY KEY (`id`) 
) ENGINE=InnoDB DEFAULT CHARSET=utf8; 

Запрос я нашел, чтобы работать очень хорошо с получением случайных записей из таблицы вопрос заключается в следующем:

SELECT id, question 
     FROM (
       SELECT @cnt := COUNT(*) + 1, 
         @lim := 10 
       FROM questions 
       ) vars 
     STRAIGHT_JOIN 
       (
       SELECT q.*, 
         @lim := @lim - 1 
       FROM questions q 
       WHERE (@cnt := @cnt - 1) 
         AND RAND() < @lim/@cnt 
       ) i 

Но сейчас я хотел бы включить в обслуживаемой таблицу, чтобы убедиться, случайные значения отбирались из вопросов, которые служили наименее. Запрос я думал только о том, как следующее:

SELECT id, question, count 
     FROM (
       SELECT @cnt := COUNT(*) + 1, 
         @lim := 10 
       FROM questions 
       ) vars 
     STRAIGHT_JOIN 
       (
       SELECT q.*, 
         s.count, 
         @lim := @lim - 1 
       FROM questions q 
       LEFT JOIN served s 
       ON s.question = q.id 
       WHERE (@cnt := @cnt - 1) 
         AND RAND() < @lim/@cnt 
       ORDER BY count ASC) i 

Проблема с этим запросом является то, что он никогда не дает свой предел 10 результатов + он никогда не дает записи я хочу. Может ли кто-нибудь подтолкнуть меня в правильном направлении?

В соответствии с запросом SQL Fiddle с некоторыми данными для тестирования: http://sqlfiddle.com/#!2/3e5ed/5. Я ожидаю, что результатом будет 10 вопросов, когда «счет» обслуживаемого пользователя 1 является наименьшим (или не существующим отключением).

Я закончил с использованием модифицированного запроса, оно должно было быть быстрым:

SELECT q.*, s1.count AS count_a, s2.count AS count_b 
FROM questions q 
LEFT JOIN served s1 
ON (s1.question = q.id AND s1.user = 1) 
LEFT JOIN served s2 
ON (s2.question = q.id AND s2.user = 2) 
WHERE q.categorie = 1 
ORDER BY IFNULL(s1.count, 0) + IFNULL(s2.count, 0) + RAND() 
LIMIT 10 
+0

Рассмотрите возможность предоставления надлежащих DDL (и/или sqlfiddle) ВМЕСТЕ С МАЛЫМ ОТБОРОМ ЛЕГИТИМАТИВНЫХ РЕЗУЛЬТАТОВ – Strawberry

+0

Я добавил URL-адрес с моими тестовыми данными + то, чего я ожидал бы сделать для моего второго запроса. – Wesley

ответ

2

Обычным способом, что люди получают случайные записи в MySQL, как это:

Чтобы получить 10 случайных записей:

SELECT * FROM questions 
ORDER BY RAND() 
LIMIT 10 

конечно, как может быть очевидно, это становится все записи в базе данных, а затем сортирует их в случайном порядке, чтобы получить 10 записей. На самом деле он просто не выбирает 10 случайных записей из базы данных. Однако этот метод легко предотвращает дублирование.

Теперь, используя ту же технику, если вы хотите, чтобы благоприятствовать менее служивших вопросы, вы могли бы сделать что-то вроде этого:

SELECT questions.* FROM questions 
LEFT JOIN served 
ON served.question = questions.id 
ORDER BY IFNULL(served.count, 0) + RAND() 
LIMIT 10 

Tweak алгоритм, чтобы изменить сумму, которую вы благоприятствовать подачу сосчитать.

Есть более эффективные способы получения случайных записей, таких как получение максимального значения первичного ключа (при условии auto_increment), затем использование RAND() на этом, а затем выбор одной записи. Вы можете использовать LIMIT 1 на случай, если RAND() возвращает пробел в ваших ключах. Тем не менее, тогда у вас могут быть дубликаты, если вы повторите этот процесс, чтобы вернуть более одной записи.

Если у вас есть смежные значения auto_increment, вы можете легко использовать PHP для выбора случайного набора ключей, а затем извлекать каждую запись по отдельности. Если они не смежны, вы сначала получаете список ключей.

Эти методы более подробно описаны в главе 16 «Случайный выбор», в книге SQL Antipatterns.

+0

Я только что исправил ошибку во втором запросе. –

+0

Со вторым запросом он по-прежнему выбирает уже обслуживаемые вопросы выше неполученных вопросов. – Wesley

+0

@Wesso, как часто? –

Смежные вопросы