2015-01-06 2 views
2

Я запускаю сервер MySQL. В моей базе данных у меня есть таблица для запросов регистрации страниц:Выберите пять самых высоких записей:

CREATE TABLE IF NOT EXISTS `log` (
    `id` int(11) NOT NULL AUTO_INCREMENT, 
    `userId` smallint(5) NOT NULL, 
    `time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP, 
    `page` varchar(127) NOT NULL 
    PRIMARY KEY (`id`) 
) ENGINE=InnoDB DEFAULT CHARSET=latin1 AUTO_INCREMENT=1 ; 

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

я придумал следующий запрос:

SELECT * 
FROM `log` 
WHERE `time` > 
    (SELECT `time` FROM `log` as l 
    WHERE `l`.`userId`=`log`.`userId` AND `l`.`page`=`log`.`page` 
    ORDER BY `time` DESC LIMIT 5,1) 
ORDER BY `time` DESC 

подзапрос должен выбрать пятую запись. Это прекрасно работает. Однако, когда пользователь не посещал определенную страницу более пяти раз, ничего не возвращается, потому что подзапрос не может найти результат (я думаю). Как я могу заставить его работать и в этом случае?

ответ

1

Во-первых, я только что чему-то научился. MySQL не разрешает limit в подзапросах с in, которые, как я думал, были перенесены на другие подобные операции (см. documentation). Но, похоже, это работает.

Примечание: ни одна из следующих работ.

Эта версия может работать:

WHERE `time` > (SELECT MIN(`time`) 
       FROM (SELECT time 
         FROM `log` l 
         WHERE `l`.`userId` = `log`.`userId` AND `l`.`page` = `log`.`page` 
         ORDER BY `time` DESC 
         LIMIT 5 
        ) l 
       ) 

Вы также можете попробовать эту формулировку:

WHERE `time` > ANY (SELECT time 
        FROM `log` l 
        WHERE `l`.`userId` = `log`.`userId` AND `l`.`page` = `log`.`page` 
        ORDER BY `time` DESC 
        LIMIT 5 
        ) 

Я не уверен, если ограничение на LIMIT в подзапросов относится к ANY ключевому слову.

EDIT:

Ни один из вышеуказанных работ из-за ограничений в MySQL. Следующие должны работать при условии, что не слишком много совпадений:

WHERE `time` > (SELECT TIME(SUBSTRING_INDEX(SUBSTRING_INDEX(GROUP_CONCAT(time ORDER BY time DESC), ',', 5), ',' -1)) 
       FROM `log` l 
       WHERE `l`.`userId` = `log`.`userId` AND `l`.`page` = `log`.`page` 
       ) 

При этом используется group_concat()/substring_index() трюк. Мне не нравится это решение, но оно должно технически работать, если у вас не так много совпадений внутри группы, что вы превысите ограничение длины для group_concat() (которое всегда может быть установлено на более высокое значение). Я действительно задаюсь вопросом, есть ли лучший способ, хотя, используя только статью where.

В качестве примечания: наиболее эффективным способом такого рода является использование переменных для перечисления строк.

+0

Большое спасибо. Я попробовал первый вариант, но MySQL жалуется на самый глубокий подзапрос, не имеющий псевдонима. После добавления псевдонима он еще жалуется: «Неизвестный столбец« log.adminId »в разделе« where clause ». Второй вариант говорит: «Эта версия MySQL еще не поддерживает« LIMIT & IN/ALL/ANY/SOME подзапрос ». – Keelan

+1

@CamilStaps. , ,Я беспокоился об этих двух проблемах. Если я подумаю о чем-нибудь еще, я отредактирую ответ. –

+0

Соединяется ли с производным запросом тоже не может быть и речи? Похоже, что первый ответ будет работать, если синтаксис правильный. –

0
LIMIT 5,1 

означает:

пропустить 5 записей, а затем принять 1

Таким образом, результаты не будут возвращены, если есть меньше, чем 6 записей.

+0

Я понимаю _why_ это не работает, я упомянул об этом даже в вопросе. Но как я могу заставить его работать, если нет 6 записей? Вот в чем вопрос. – Keelan

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