我正在尝试按出生日期进行分组,并根据结果计算,使用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。)
答案 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();对于复杂的查询,它通常值得绕过蛋糕,但正如我所说,结果在查询时是有意义的。