最后x个博客条目 - 但每个用户只有一次

时间:2012-01-03 01:37:39

标签: mysql cakephp cakephp-2.0

我想显示一个包含最后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

如果我省略了subqery限制:

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

4 个答案:

答案 0 :(得分:1)

使用子查询似乎可行 - 使用这个小技巧:

$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

答案 1 :(得分:0)

在不知道表结构的情况下,这是相当困难的,但是,这应该给你一个起点:

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个最新帖子的列表。然后,您将需要第二个查询来获取内容。

答案 2 :(得分:0)

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获取与这些条目相关的剩余信息。

答案 3 :(得分:0)

如果你尝试使用这个视图怎么样,但它有点奇怪的解决方案,如:

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;

这有点奇怪,因为你仍然需要创建视图,但是当它被userid分组时你会确定它已经按发布日期对DESC进行了排序,因为视图已经像虚拟表一样,但它会是理想的,如果你指定哪些列你真正需要,因为如果你只是使用'*'来显示表中的所有列,那么某些列名可能会有冲突(来自其他表的列名相同)。

我看到您在查询时遇到的新更新问题出现了,因为您在IN语句中使用了2个列(它只接受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子查询'