2011-09-20 3 views
1

У меня есть следующая таблица.Zend DB Присоединитесь к двум строкам из одной таблицы

--------------------------------------------- 
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 | 
----------------------------------------------- 

Может кто-нибудь сказать мне, что я делаю неправильно?

+1

Мне не сразу понятно, какой SQL вы пытаетесь создать через Zend_Db_Select. Можете ли вы добавить SQL к вопросу? Кроме того, вы можете использовать метод Zend_Db_Select :: toString для печати SQL, который будет выполняться при запросе, что может помочь вам отладить. –

ответ

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 вообще в этом случае, при условии, что вы не имеете несколько начинается и заканчивается для каждого действия.

Кстати, этот тип сложности является одной из причин, по которой обычно рекомендуется хранить данные о начале и конце события в одной строке, причем начало в одном столбце и конец во втором столбце.

+0

Использование GROUP BY action_id дает мне результаты, которые мне нужны. Я мог бы также использовать joinRight, но это также дает мне результаты только с одной строкой. Мне нужны результаты, когда две строки имеют один и тот же action_id. Возможно, лучшее решение для хранения даты начала и окончания в одной строке. Я строю систему регистрации/проверки. – Stuiterbal

+0

Правильное соединение не возвращает строку, в которой action_id еще не делится с другой строкой. Мне тоже нужны. Таким образом, LEFT JOIN в сочетании с GROUP BY делает трюк. – Stuiterbal

+0

Это нормально, но вы получили часть, которую я рассмотрел, о тестировании fot c1.dt

Смежные вопросы