yii - 为按日期排序的每个类型字段选择前五个记录

时间:2014-07-21 07:00:52

标签: php mysql sql yii greatest-n-per-group

我有一个包含以下字段的表格:

Advertisements (
  id bigint 20,
  name varchar 255,
  content text,
  creator_id bigint 20,
  type_id bigint 255 DEFAULT VALUE IS NULL,
  create_date bigint 20
)

我需要按类型选择前5个记录按日期排序(我需要最新的记录)。另外,我想将结果与CActiveDataProvider一起使用。除了sql查询器之外,有没有办法使用模型函数或yii集成函数获取记录(原因:我还需要在Advertisements类中提供关系)?

更新 其他表:

Users (
  id bigint 20,
  login varchar 255,
  password varchar 255,
  email varchar 255,
  date_of_registration bigint 20,
  status varchar 255
)
(The table with types)
Items (
  id bigint 20,
  item varchar 255,
  link varchar 255
)
Comments (
  id bigint 20,
  creator_id bigint 20,
  ......
  topic_id bigint 20
)
Rating (
  id bigint 20,
  creator_id bigint 20,
  ......
  topic_id bigint 20
)

如果一个类型的记录数超过5,那么我想在type_id订购的广告中为每个create_date提供最新记录,然后只选择最新的5.还要我登录创建者+评论数量+评级表完全?我知道SQL可以解决,但我没有任何想法,因为我不擅长SQL。任何帮助表示赞赏。感谢所有试图提供帮助的人。

4 个答案:

答案 0 :(得分:4)

根据詹姆斯·K·洛登的暗示,在一套谷歌搜索之后我得到了下一个答案:

SELECT t.id,
        t.name,
        t.content,
        i.item,
        c.context
FROM (SELECT tbl.id,
            tbl.name,
            tbl.content,
            tbl.owner_id,
            tbl type_id,
            CASE 
                WHEN tbl.type_id = @category THEN @rownum := @rownum + 1
                ELSE @rownum := 1
            END AS num,
            @category := tbl.type_id
        FROM Advertisements tbl
        JOIN (SELECT @rownum := 0, @category := NULL) r
        ORDER BY tbl.type_id, tbl.create_date DESC) t
LEFT JOIN Items i ON t.type_id = i.id
LEFT OUTER JOIN Comments c ON c.topic_id = t.id
WHERE t.num <= 5

答案 1 :(得分:2)

如果我理解你,这样的事情应该有效(未经测试)。你应该有一个索引(type_id,date)

// Extract unique types. Perhaps you have table `type` and should do this other way but since you provided info only on Advertisements table I'm using what I got
$types = YII::app()->db->createCommand("SELECT DISTINCT type_id FROM Advertisements;")->queryColumn();

$resultsAll = array();
foreach ($types as $type_id) {
    $results = Advertisments::model()->findAll(
            array("condition" => "type_id = $type_id", "order" => "date DESC", "limit" => 5)
    );
    $resultsAll = array_merge($resultsAll, $results);
}

$dataProvider = new CArrayDataProvider($resultsAll, array());

答案 2 :(得分:1)

我不确定我理解每种类型的是什么意思,或者为什么你不想使用SQL来做SQL的设计。

如果您使用canonical query对类型进行排名,

select a.id, a.name, a.content, 
       a.creator_id, a.type_id, count(b.id) as Rank
from Advertisements as a join Advertisements as b
on    a.id = b.id
and   a.name = b.name
and   a.content = b.content
and   a.creator_id = b.creator_id
and   a.type_id >= b.type_id
group by a.id, a.name, a.content, a.creator_id, a.type_id
having count(b.id) <= 5

和Advertisements由GROUP BY子句中的列索引,查询应在 O(n log n)时间运行。在大多数情况下,这与 O(n)无法区分,这很难被击败。

如果您关注性能,可能需要重新考虑列大小。我不知道bigint 20是什么,但MySQL bigint是64位。你真的期待超过40亿的创作者或类型,你确定这个名字必须超过30个字符吗?如果您现在制作的选项限制太多,所有这些选择都可以在以后更改,但是太大的选择会在第1天降低性能。

答案 3 :(得分:1)

使用CDBCriteria。例如:

function actionIndex()
{
    ...
    $_criteria = new CDbCriteria();
    $_criteria->order = 'create_date DESC';
    $_criteria->limit = 5;

    $_ads = new CActiveDataProvider('Advertisements', array('criteria' => $_criteria));

    ...

    $this->render('index', array(
        ...
        'ads' => $_ads,
        ...
    );
}