Попробуйте этот запрос, без группы по :
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 и c2 указывают на одну и ту же строку.
Затем вы используете GROUP BY, которая объединяет несколько строк в одну, но MySQL выбирает произвольную строку из группы. (Это неоднозначный запрос, и если вы SET SQL_MODE='ONLY_FULL_GROUP_BY'
получите сообщение об ошибке.)
Таким образом, ваше самообъединение и только GROUP BY возвращают c1 и c2, которые на самом деле являются одной и той же строкой.
Чтобы это исправить, вы должны добавить условие, что c1.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, при условии, что у вас нет нескольких начальных и конечных значений для каждого действия.
Кстати, этот тип сложности является одной из причин, по которой обычно рекомендуется хранить данные начала / окончания события в одной строке, где начало в одном столбце, а конец во втором столбце.