Последние x записей в блоге - но только один раз на пользователя - PullRequest
2 голосов
/ 03 января 2012

Я хотел бы отобразить окно с последними x (скажем, 5) записями в блоге.Но я бы хотел избежать того, чтобы один очень активный пользователь был указан дважды.

Моя попытка сводится к:

    $stats['blog'] = $this->User->Blog->find('all', array(
        'order'=>array('Blog.published' => 'DESC'), 
        'conditions' => array('Blog.status' => 1), 
        'contain' => array('User.username'),
        'group' => array('User.id'),
        'limit' => 5,
    ));

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

SELECT * 
FROM `comm_blogs` AS `Blog` 
LEFT JOIN `comm_users` AS `User` ON (`Blog`.`user_id` = `User`.`id`) 
WHERE `Blog`.`status` = 1 
GROUP BY `User`.`id` 
ORDER BY `Blog`.`published` DESC LIMIT 5

Поэтому результат почти всегда неверен, потому что новые записи в блоге никогда не отображаются, еслиЭтот пользователь уже писал о чем-то еще в прошлом.

Как мне сначала отсортировать по опубликованному DESC перед группировкой?Или есть другой удобный способ?Thx

Структура таблиц:

пользователи:

- id
- username

блоги:

- id
- user_id
- published (datetime)
- title
- content
- status

@ gerald:

Похоже, MYSQl не нравятся такие подзапросы:

Синтаксическая ошибка или нарушение прав доступа: 1235 Эта версия MySQL еще не поддерживает подзапрос LIMIT & IN / ALL / ANY / SOME '

SELECT `User`.`id`, `User`.`username`, `Blog`.`id`, `Blog`.`headline`, `Blog`.`published`, `UserInfo`.`gender`, `UserInfo`.`id` FROM `comm_blogs` AS `Blog` 
LEFT JOIN `comm_users` AS `User` ON (`Blog`.`user_id` = `User`.`id`) 
LEFT JOIN `comm_user_infos` AS `UserInfo` ON (`Blog`.`user_id` = `UserInfo`.`id`) 
WHERE `User`.`active` = '1' AND `Blog`.`status` = 1 AND `Blog`.`id` IN (
    SELECT `LastBlog`.`id`, MAX(`LastBlog`.`published`) as last 
    FROM comm_blogs AS LastBlog WHERE `LastBlog`.`status` = 1 
    GROUP BY `LastBlog`.`user_id` ORDER BY last DESC LIMIT 5
) 
ORDER BY `Blog`.`published` DESC

Если я опущу ограничение подзапроса:

 Cardinality violation: 1241 Operand should contain 1 column(s) 

Ответы [ 4 ]

1 голос
/ 13 августа 2012

Использование подзапроса, кажется, работает - с этим небольшим трюком:

$options = array(
    'fields' => array('MAX(SubBlog.created)'),
    'conditions' => array('SubBlog.user_id = Blog.user_id')
);
$subquery = $this->subquery('all', $options);

$options = array(
    'order'=>array($this->alias.'.published' => 'DESC'),
    'conditions' => array(
        'User.active' => 1,
        'Blog.status' => self::STATUS_ACTIVE, 
        'Blog.published = ' . $subquery
    ),
    'contain' => array('User.username'),
    'fields' => array(
        'User.id',  'User.username', 
        'Blog.id', 'Blog.headline', 'Blog.published'
    ),
    'limit' => $limit,
);
return $this->find('all', $options);

subquery () - это метод AppModel: https://github.com/dereuromark/tools/blob/2.0/Lib/MyModel.php#L405

0 голосов
/ 03 января 2012
SELECT * 
FROM `comm_blogs` AS `Blog` 
LEFT JOIN `comm_users` AS `User` ON (`Blog`.`user_id` = `User`.`id`) 
WHERE `Blog`.`status` = 1 
    AND blog.id IN (
        SELECT lastblog.id, max(lastblog.published) as lastpost
        FROM comm_blogs AS lastblog
        WHERE lastblog.status = 1
        GROUP BY lastblog.user_id
        ORDER BY lastpost DESC LIMIT 5)

Я думаю, что это будет работать;однако я НЕ проверял SQL.

Как видите, внутренний SQL получает последние пять записей блога с уникальными пользователями.Внешний SQL получает оставшуюся информацию, связанную с этими записями, на основе blog.id.

0 голосов
/ 06 января 2012

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

CREATE VIEW comm_blogs_publish_desc AS SELECT `User`.`id` AS `userid`, * 
FROM `comm_blogs` AS `Blog` 
LEFT JOIN `comm_users` AS `User` ON (`Blog`.`user_id` = `User`.`id`) 
WHERE `Blog`.`status` = 1 
ORDER BY `Blog`.`published` DESC;

Тогда вы можете использовать его уже к исходному запросу:

SELECT * 
FROM comm_blogs_publish_desc
GROUP BY `userid` LIMIT 5;

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

Я видел, что в новом обновлении, которое вы сделали для запроса, проблема возникает из-за того, что вы используете 2 столбца в операторе IN (он принимает только 1 столбец для подзапроса и возвращает ошибку, если вы используете 2 или более столбцов для него, что приводит к количеству элементовпроблема) обновите его следующим образом:

SELECT `User`.`id`, `User`.`username`, `Blog`.`id`, `Blog`.`headline`, `Blog`.`published`, `UserInfo`.`gender`, `UserInfo`.`id` FROM `comm_blogs` AS `Blog` 
LEFT JOIN `comm_users` AS `User` ON (`Blog`.`user_id` = `User`.`id`) 
LEFT JOIN `comm_user_infos` AS `UserInfo` ON (`Blog`.`user_id` = `UserInfo`.`id`) 
WHERE `User`.`active` = '1' AND `Blog`.`status` = 1 AND `Blog`.`id` IN (
    SELECT `LastBlog`.`id` 
    FROM comm_blogs AS LastBlog WHERE `LastBlog`.`status` = 1 
    GROUP BY `LastBlog`.`user_id` ORDER BY last DESC
) 
ORDER BY `Blog`.`published` DESC LIMIT 0, 5;

и затем передайте лимит в основной запрос вместоПодзапрос, чтобы избежать последней проблемы: Синтаксическая ошибка или нарушение прав доступа: 1235 Эта версия MySQL еще не поддерживает подзапрос LIMIT & IN / ALL / ANY / SOME '

;)

0 голосов
/ 03 января 2012

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

SELECT `User`.`id`, max(`Blog`.`published`) 
FROM `comm_blogs` AS `Blog` 
LEFT JOIN `comm_users` AS `User` ON (`Blog`.`user_id` = `User`.`id`) 
WHERE `Blog`.`status` = 1 
GROUP BY `User`.`id` 
ORDER BY 2 DESC LIMIT 5

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

...