我有下表。
---------------------------------------------
check_id | action_id | user_id | dt |
---------------------------------------------
1 | 1 | 6 | 2011-09-17 |
2 | 1 | 6 | 2011-09-18 |
3 | 3 | 6 | 2011-09-19 |
4 | 3 | 6 | 2011-09-20 |
---------------------------------------------
我想查询此表并获得以下结果。
-----------------------------------------------
action_id | user_id | dt_start | dt_end |
-----------------------------------------------
1 | 6 | 2011-09-17 | 2011-09-18 |
3 | 6 | 2011-09-19 | 2011-09-20 |
-----------------------------------------------
所以我使用以下查询。
$checks->select()
->from(array('c1' => 'checks'), array('dt as dt_start')
->joinLeft(array('c2' => 'checks'), 'c1.action_id = c2.action_id', array('dt as dt_end')
->where('c1.user_id = ?', $userId)
->group('c1.action_id')
但是这给了我以下结果。
-----------------------------------------------
action_id | user_id | dt_start | dt_end |
-----------------------------------------------
1 | 1 | 2011-09-17 | 2011-09-17 |
1 | 3 | 2011-09-19 | 2011-09-19 |
-----------------------------------------------
有人能告诉我我做错了吗?
答案 0 :(得分:1)
尝试此查询,不带的小组:
SELECT *
FROM checks c1 LEFT OUTER JOIN checks c2 ON c1.action_id = c2.action_id
WHERE c1.user_d = 6
你会看到你得到的比赛是c1.dt< c2.dt,这就是你想要的。 但你也得到匹配c1.dt> c2.dt,其中c1.dt = c2.dt. 也就是说,自联接包括c1和c2指向同一行的结果。
然后使用GROUP BY将多行折叠成一行,但MySQL选择该组中的任意行。 (这是一个含糊不清的查询,如果你SET SQL_MODE='ONLY_FULL_GROUP_BY'
,你会收到错误。)
所以你的自我加入和GROUP BY 只返回实际上是同一行的c1和c2。
要解决此问题,您应该添加一个条件c1.dt< c2.dt.
$checks->select()
->from(array('c1' => 'checks'), array('dt as dt_start')
->joinLeft(array('c2' => 'checks'),
'c1.action_id = c2.action_id AND c1.dt < c2.dt',
array('dt as dt_end')
->where('c1.user_id = ?', $userId)
在这种情况下,您可能根本不需要GROUP BY,假设您没有多个开始和结束每个操作。
顺便说一下,这种复杂性是一个原因,通常建议将事件开始/结束数据存储在一行中,一行开始,第二列结束。