Слияние двух строк с уникальным атрибутом

Это пример вывода моей таблицы (time_entries)

user_name| entry_type   | entry_datetime
 User1   |   Time In    | 28-JUL-13  16:40:40
 User1   |   Time Out   | 28-JUL-13  16:40:41
 User2   |   Time In    | 28-JUL-13  16:41:13
 User2   |   Time Out   | 28-JUL-13  16:41:15
 User1   |   Time In    | 27-JUL-13  16:42:30
 User1   |   Time Out   | 27-JUL-13  16:42:34
 User2   |   Time In    | 27-JUL-13  16:43:32
 User2   |   Time Out   | 27-JUL-13  16:43:35

Теперь я использовал этот запрос

SELECT te.user_name, te.entry_name, MAX(te.entry_datetime) AS date
FROM time_entries AS te
GROUP BY te.entry_type, te.user_name

и результатом этого является

user_name| entry_type   | entry_datetime
 User1   |   Time In    | 28-JUL-13  16:40:40
 User1   |   Time Out   | 28-JUL-13  16:40:41
 User2   |   Time In    | 28-JUL-13  16:41:13
 User2   |   Time Out   | 28-JUL-13  16:41:15

ВОПРОС: Есть ли способ объединить имя пользователя с тем же именем в 1 строке? и вывести что-то вроде этого.

user_name|  Date     | Timein   | Timeout
User1    | 28-JUL-13 | 16:40:40 | 16:40:41
User2    | 28-JUL-13 | 16:41:13 | 16:41:15

Я немного запутался, как это сделать.

Ответ 1

Я бы подошел к нему таким образом:

  • Создайте запрос, который возвращает время "времени" для каждого пользователя и даты.
  • Создайте запрос типа # 1, который возвращает время тайм-аута для каждого пользователя и даты.
  • Объедините результаты вместе, используя LEFT JOIN.

Конечный результат:

SELECT te.user_name, LEFT(te.entry_datetime, 10) AS entry_date, cin.entry_datetime AS timein, cout.entry_datetime AS timeout
FROM time_entries te
LEFT JOIN (SELECT user_name, LEFT(entry_datetime, 10) AS entry_date, MIN(entry_datetime) AS entry_datetime
    FROM time_entries
    WHERE entry_type = 'Time In'
    GROUP BY user_name, entry_date
) cin ON cin.user_name = te.user_name AND cin.entry_date = LEFT(te.entry_datetime, 9)
LEFT JOIN (SELECT user_name, LEFT(entry_datetime, 10) AS entry_date, MAX(entry_datetime) AS entry_datetime
    FROM time_entries
    WHERE entry_type = 'Time Out'
    GROUP BY user_name, entry_date
) cout ON cout.user_name = te.user_name AND cout.entry_date = LEFT(te.entry_datetime, 10)
GROUP BY te.user_name, entry_date;

Демо

Btw, я использую LEFT() здесь, потому что ваш столбец не содержит фактического формата даты, который понимает MySQL.

Ответ 2

Как насчет этого?

select
user_name,
date(entry_datetime) as date,
min(entry_datetime) as Timein,
max(entry_datetime) as Timeout
from (
  SELECT te.user_name, te.entry_name, MAX(te.entry_datetime) AS date
  FROM time_entries AS te
  GROUP BY te.entry_type, te.user_name) src
group by user_name, date

Ответ 3

Вот php-стороннее решение о том, как вы можете обрабатывать данные.

function transformArray($data) {
   $final = array();
   foreach ($data as $key => $row) {
      if (!isset($final[$row['u']])) $final[$row['u']] = array('user' => $row['u']);
      $final[$row['u']][$row['t']] = date("H:i:s", strtotime($row['entrydatetime']));
      $final[$row['u']]['date'] = date("d:M:y", strtotime($row['entrydatetime']));
   }

   return $final;
}

Вы можете передать ему набор данных, который выглядит следующим образом: вы можете настроить имена полей как nessecery.

$data = array(
         array('u' => 'user1', 't' => 'TimeIn', 'entrydatetime' => '28-JUL-13  16:40:40'),
         array('u' => 'user1', 't' => 'TimeOut', 'entrydatetime' => '28-JUL-13  16:40:40'),
         array('u' => 'user2', 't' => 'TimeIn', 'entrydatetime' => '28-JUL-13  16:40:40'),
         array('u' => 'user2', 't' => 'TimeOut', 'entrydatetime' => '28-JUL-13  16:40:40')
);

Ответ 4

Это выполнит задание:

SELECT te.user_name,DATE(te.entry_datetime) as Date,te.entry_datetime AS TimeIn,te2.entry_datetime AS TimeOut
FROM time_entries te
JOIN time_entries te2 ON te.user_name=te2.user_name AND te2.entry_type="Time Out" AND
  te2.entry_datetime=
  (
    SELECT 
      entry_datetime
    FROM 
      time_entries
    WHERE entry_type='Time Out' AND user_name=te.user_name AND entry_datetime > te.entry_datetime
    ORDER BY 
      ABS(TIMESTAMPDIFF(SECOND, te.entry_datetime, `entry_datetime`))
    LIMIT 1
  )
WHERE te.entry_type="Time In";

Изменить: небольшое объяснение:

Он выбирает имя_пользователя, дату и время_документа для части TimeIn. Затем он присоединяется к одной таблице, чтобы получить часть времени и времени Time Out. Он делает это, выбирая запись с типом "Time Out" с тем же именем пользователя и датой, которая ближе всего к дате времени Time In.

Ответ 5

select
user_name,
dATE(entry_datetime) AS date1,
min(entry_datetime) as Timein,
max(entry_datetime) as Timeout
from time_entries
group by user_name, date1

SqlFiddleDemo

Этот запрос будет работать соответствующим образом, если для каждого Time_In существует соответствующий тайм-аут. и каждый новый тип ввода должен начните с time_in