CakePHP中的GROUP和COUNT()年龄

时间:2010-01-26 19:15:07

标签: mysql cakephp

我正在尝试按出生日期进行分组,并根据结果计算,使用CakePHP。这是我的问题。

$data = $this->User->find('all', array(
    'fields' => array(
        "DATE_FORMAT(NOW(), '%Y') - DATE_FORMAT(User.dob, '%Y') - (DATE_FORMAT(NOW(), '00-%m-%d') < DATE_FORMAT(User.dob, '00-%m-%d')) AS age",
        'COUNT(id)'
    ),
    'group' => 'age'
));

到目前为止,这么好。字段User.dob是出生日期,它是DATETIME字段。

事情是,它返回这样的东西:

Array
(
    [0] => Array
        (
            [0] => Array
                (
                    [age] => 9
                    [COUNT(id)] => 1
                )

        )

    [1] => Array
        (
            [0] => Array
                (
                    [age] => 10
                    [COUNT(id)] => 1
                )

        )

    [2] => Array
        (
            [0] => Array
                (
                    [age] => 11
                    [COUNT(id)] => 1
                )

        )

    [3] => Array
        (
            [0] => Array
                (
                    [age] => 12
                    [COUNT(id)] => 8
                )

        )

    [4] => Array
        (
            [0] => Array
                (
                    [age] => 13
                    [COUNT(id)] => 1
                )

        )

当然必须有更好的方法。

我甚至无法过滤它。此代码抛出错误。未知列'年龄'

$data = $this->User->find('all', array(
    'conditions' => array('age >' => 20),
    'fields' => array(
        "DATE_FORMAT(NOW(), '%Y') - DATE_FORMAT(User.dob, '%Y') - (DATE_FORMAT(NOW(), '00-%m-%d') < DATE_FORMAT(User.dob, '00-%m-%d')) AS age",
        'COUNT(id)'
    ),
    'group' => 'age'
));

顺便说一下,这些是查询。

SELECT DATE_FORMAT(NOW(), '%Y') - DATE_FORMAT(User.dob, '%Y') - (DATE_FORMAT(NOW(), '00-%m-%d') < DATE_FORMAT(User.dob, '00-%m-%d')) AS age, COUNT(id) FROM `users` AS `User` WHERE 1 = 1 GROUP BY age 

(年龄计算例程见matt's blog。)

4 个答案:

答案 0 :(得分:4)

您获得的结果几乎是CakePHP产生的最佳结果。

为简化此操作,您应该使用Set::combine重新索引数组。

您需要致电$data = Set::combine($data, '{n}.0.age', '{n}.0.COUNT(id)');

这将返回一个以年龄为索引的数组,并计为值:

Array
(
    [9] => Array
        (
            [COUNT(id)] => 1

        )

    [10] => Array
        (
            [COUNT(id)] => 1

        )
    ...
)

数组中额外深度的原因是如果你不使用计算字段,cake会使用模型作为内部数组的键,这样你就可以将多个模型作为字段放入,并将它们分成不同的阵列。当您使用计算字段时,它保持相同的结构,但不知道模型,因此必须将其放在一般数组中。

因此,假设您希望按男/女分组,并且您有一个User.sex字段,该字段不是计算字段。

$data = $this->User->find('all', array(
    'fields' => array(
        "User.sex"
        "DATE_FORMAT(NOW(), '%Y') - DATE_FORMAT(User.dob, '%Y') - (DATE_FORMAT(NOW(), '00-%m-%d') < DATE_FORMAT(User.dob, '00-%m-%d')) AS age",
        'COUNT(User.id) AS [count]' // n.b. you have to give aliases normally
    ),
    'group' => 'age', 'User.sex'
));

这将返回(类似):

Array
(
    [0] => Array
        (
            [User] => Array
                (
                    [sex] => Male
                )
            [0] => Array
                (
                    [age] => 4
                    [count] => 1
                )

        )

    [1] => Array
        (
            [User] => Array
                (
                    [sex] => Female
                )
            [0] => Array
                (
                    [age] => 10
                    [count] => 1
                )

        )

    [2] => Array
        (
            [User] => Array
                (
                    [sex] => Male
                )
            [0] => Array
                (
                    [age] => 10
                    [count] => 1
                )

        )
)

因此,为了保持一致性,即使您只使用计算字段

,额外深度也始终存在

答案 1 :(得分:3)

首先,我认为您不需要'COUNT(id)'。您可以随时轻松地在PHP中计算:

foreach($data as $group){echo count($group);}

要过滤,请检查原始字段,而不是派生字段:

'conditions'=>array('User.dob >' => date('Y-m-d', strtotime('-20 years')))

无论如何,如果你需要,总会有Model->query()

答案 2 :(得分:2)

我认为你在结果数组上得到了数字索引,因为你添加的字段还没有由CakePHP生成。 CakePHP通常在SQL中生成更像这样的查询(和字段名称):

SELECT `Item`.`id`, `Item`.`name` FROM `items` AS `Item` WHERE 1 = 1 ORDER BY `Item`.`name` ASC

如果您希望CakePHP更好地理解和格式化从MySQL回来的结果,那么在向查询中添加自定义元素时,您应该尝试模仿CakePHP的字段命名约定:

$age = "DATE_FORMAT(NOW(), '%Y') - DATE_FORMAT(User.dob, '%Y') - (DATE_FORMAT(NOW(), '00-%m-%d') < DATE_FORMAT(User.dob, '00-%m-%d'))";
$data = $this->User->find('all', array(
    'conditions' => array('User.age >' => 20),
    'fields' => array(
        $age . ' AS `User`.`age`',
        'COUNT(id) AS `User`.`count`'
    ),
    'group' => 'User.age'
));

也许这会让你更幸运地获得工作条件。 :)

答案 3 :(得分:-1)

你使用的是什么版本的cakephp?我认为1.2的早期版本和我认为1.1的所有版本都有分组和复杂聚合查询的问题 - 后来是resolved。也就是说结果看起来像蛋糕正在运行你所要求的:返回一个年龄列表,以及那个年龄段的用户数。

无论如何,为什么要在mysql中运行它并绑定你的数据库服务器?获取原始值,并重写查询以计算php中的聚合,并将其附加到返回的数组。

如果你必须使用mysql,总会有$ this-&gt; query();对于复杂的查询,它通常值得绕过蛋糕,但正如我所说,结果在查询时是有意义的。