MySQL: Display Users and Duration in Matrix Timesheet

Given Data
Activities by employees with start dates and end dates in a mySQL database.

Objective
copyraw
User		Mon	Tue	Wed	Thu	Fri	Sat	Sun	Total
--------------- ------- ------- ------- ------- ------- ------- ------- ---------
Me		09:00	07:30	08:00	07:00	06:00	00:00	02:00   36:00
Myself		07:30	07:30	07:30	07:30	07:00	00:00	00:00   37:00
I		03:45	03:30	03:00	03:30	00:00	00:00	00:00   23:00
  1.  User        Mon    Tue    Wed    Thu    Fri    Sat    Sun    Total 
  2.  --------------- ------- ------- ------- ------- ------- ------- ------- --------- 
  3.  Me        09:00    07:30    08:00    07:00    06:00    00:00    02:00   36:00 
  4.  Myself        07:30    07:30    07:30    07:30    07:00    00:00    00:00   37:00 
  5.  I        03:45    03:30    03:00    03:30    00:00    00:00    00:00   23:00 

Method
copyraw
1. Generate query to list all valid users
2. Generate query which will total hours per week per user
3. Combine in a loop to run query against each user.
  1.  1. Generate query to list all valid users 
  2.  2. Generate query which will total hours per week per user 
  3.  3. Combine in a loop to run query against each user. 

Post-Notes (Update 2012)


The performance of the previous version of this query was pretty abysmal and worse if you want to list users who have no associated activities (outer join but all-in-one query). The DBA responsible for the system asked me to look at ways of reducing the load on the server and the solution was to separate both tables (activities and users) rather than trying to join these, and to let PHP do a bit more of the work:

User Query
copyraw
$t_value = $the_team_id_that_i_want_to_look_at;

$user_list_query="
SELECT DISTINCT
	u.UserID,
	CONCAT(u.FirstName, ' ', u.LastName) AS 'Fullname'
FROM 
	Users u
WHERE
	u.StatusType NOT IN ('expired','guest','test account')
AND 
	u.TeamID='$t_value'
ORDER BY 
	CONCAT(u.FirstName, ' ', u.LastName)
";
  1.  $t_value = $the_team_id_that_i_want_to_look_at
  2.   
  3.  $user_list_query=" 
  4.  SELECT DISTINCT 
  5.      u.UserID, 
  6.      CONCAT(u.FirstName, ' ', u.LastName) AS 'Fullname' 
  7.  FROM 
  8.      Users u 
  9.  WHERE 
  10.      u.StatusType NOT IN ('expired','guest','test account') 
  11.  AND 
  12.      u.TeamID='$t_value' 
  13.  ORDER BY 
  14.      CONCAT(u.FirstName, ' ', u.LastName) 
  15.  "

Activity Query
copyraw
$w_value = $number_of_weeks_from_now; 
// usually 0 for this week, 1 for the week before, 2 for the week before that...

$timesheet_query_per_user="
SELECT DISTINCT
	s.StaffUserID AS 'UserID',
	(
		SELECT SEC_TO_TIME(SUM(TIME_TO_SEC(s.EstimateTimeFinish)-TIME_TO_SEC(s.DateTimeCreated))) AS SecondsElapsed
		FROM StaffActivities s
		WHERE WEEKDAY(s.DateTimeCreated)=0
		AND YEARWEEK(s.DateTimeCreated)=YEARWEEK(NOW()) - $w_value
		AND s.StaffUserID='STRING_TO_REPLACE_PER_ITERATION'
	) AS 'Monday',
	(
		SELECT SEC_TO_TIME(SUM(TIME_TO_SEC(s.EstimateTimeFinish)-TIME_TO_SEC(s.DateTimeCreated))) AS SecondsElapsed
		FROM StaffActivities s
		WHERE WEEKDAY(s.DateTimeCreated)=1
		AND YEARWEEK(s.DateTimeCreated)=YEARWEEK(NOW()) - $w_value
		AND s.StaffUserID='STRING_TO_REPLACE_PER_ITERATION'
	) AS 'Tuesday',
	(
		SELECT SEC_TO_TIME(SUM(TIME_TO_SEC(s.EstimateTimeFinish)-TIME_TO_SEC(s.DateTimeCreated))) AS SecondsElapsed
		FROM StaffActivities s
		WHERE WEEKDAY(s.DateTimeCreated)=2
		AND YEARWEEK(s.DateTimeCreated)=YEARWEEK(NOW()) - $w_value
		AND s.StaffUserID='STRING_TO_REPLACE_PER_ITERATION'
	) AS 'Wednesday',
	(
		SELECT SEC_TO_TIME(SUM(TIME_TO_SEC(s.EstimateTimeFinish)-TIME_TO_SEC(s.DateTimeCreated))) AS SecondsElapsed
		FROM StaffActivities s
		WHERE WEEKDAY(s.DateTimeCreated)=3
		AND YEARWEEK(s.DateTimeCreated)=YEARWEEK(NOW()) - $w_value
		AND s.StaffUserID='STRING_TO_REPLACE_PER_ITERATION'
	) AS 'Thursday',
	(
		SELECT SEC_TO_TIME(SUM(TIME_TO_SEC(s.EstimateTimeFinish)-TIME_TO_SEC(s.DateTimeCreated))) AS SecondsElapsed
		FROM StaffActivities s
		WHERE WEEKDAY(s.DateTimeCreated)=4
		AND YEARWEEK(s.DateTimeCreated)=YEARWEEK(NOW()) - $w_value
		AND s.StaffUserID='STRING_TO_REPLACE_PER_ITERATION'
	) AS 'Friday',
	(
		SELECT SEC_TO_TIME(SUM(TIME_TO_SEC(s.EstimateTimeFinish)-TIME_TO_SEC(s.DateTimeCreated))) AS SecondsElapsed
		FROM StaffActivities s
		WHERE WEEKDAY(s.DateTimeCreated)=5
		AND YEARWEEK(s.DateTimeCreated)=YEARWEEK(NOW()) - $w_value
		AND s.StaffUserID='STRING_TO_REPLACE_PER_ITERATION'
	) AS 'Saturday',
	(
		SELECT SEC_TO_TIME(SUM(TIME_TO_SEC(s.EstimateTimeFinish)-TIME_TO_SEC(s.DateTimeCreated))) AS SecondsElapsed
		FROM StaffActivities s
		WHERE WEEKDAY(s.DateTimeCreated)=6
		AND YEARWEEK(s.DateTimeCreated)=YEARWEEK(NOW()) - $w_value
		AND s.StaffUserID='STRING_TO_REPLACE_PER_ITERATION'
	) AS 'Sunday',
	(
		SELECT SEC_TO_TIME(SUM(TIME_TO_SEC(s.EstimateTimeFinish)-TIME_TO_SEC(s.DateTimeCreated))) AS SecondsElapsed
		FROM StaffActivities s
		WHERE YEARWEEK(s.DateTimeCreated)=YEARWEEK(NOW()) - $w_value
		AND s.StaffUserID='STRING_TO_REPLACE_PER_ITERATION'
	) AS 'Total',
	(
		SELECT ROUND((SUM(TIME_TO_SEC(s.EstimateTimeFinish)-TIME_TO_SEC(s.DateTimeCreated))/26640),2) AS SecondsElapsed
		FROM StaffActivities s
		WHERE YEARWEEK(s.DateTimeCreated)=YEARWEEK(NOW()) - $w_value
		AND s.StaffUserID='STRING_TO_REPLACE_PER_ITERATION'
	) AS 'Total Days'
FROM StaffActivities s
WHERE s.StaffUserID='STRING_TO_REPLACE_PER_ITERATION'
";

// POST-NOTES: 
// 26640 is 7 hours 24 minutes (our working day).  
// 4 days times 7:30 and 1 day of 7:00 will result in 37:00 total.
// Our working week starts on Monday 00:00 and ends on Sunday 23:59.
  1.  $w_value = $number_of_weeks_from_now
  2.  // usually 0 for this week, 1 for the week before, 2 for the week before that... 
  3.   
  4.  $timesheet_query_per_user=" 
  5.  SELECT DISTINCT 
  6.      s.StaffUserID AS 'UserID', 
  7.      ( 
  8.          SELECT SEC_TO_TIME(SUM(TIME_TO_SEC(s.EstimateTimeFinish)-TIME_TO_SEC(s.DateTimeCreated))) AS SecondsElapsed 
  9.          FROM StaffActivities s 
  10.          WHERE WEEKDAY(s.DateTimeCreated)=0 
  11.          AND YEARWEEK(s.DateTimeCreated)=YEARWEEK(NOW()) - $w_value 
  12.          AND s.StaffUserID='STRING_TO_REPLACE_PER_ITERATION' 
  13.      ) AS 'Monday', 
  14.      ( 
  15.          SELECT SEC_TO_TIME(SUM(TIME_TO_SEC(s.EstimateTimeFinish)-TIME_TO_SEC(s.DateTimeCreated))) AS SecondsElapsed 
  16.          FROM StaffActivities s 
  17.          WHERE WEEKDAY(s.DateTimeCreated)=1 
  18.          AND YEARWEEK(s.DateTimeCreated)=YEARWEEK(NOW()) - $w_value 
  19.          AND s.StaffUserID='STRING_TO_REPLACE_PER_ITERATION' 
  20.      ) AS 'Tuesday', 
  21.      ( 
  22.          SELECT SEC_TO_TIME(SUM(TIME_TO_SEC(s.EstimateTimeFinish)-TIME_TO_SEC(s.DateTimeCreated))) AS SecondsElapsed 
  23.          FROM StaffActivities s 
  24.          WHERE WEEKDAY(s.DateTimeCreated)=2 
  25.          AND YEARWEEK(s.DateTimeCreated)=YEARWEEK(NOW()) - $w_value 
  26.          AND s.StaffUserID='STRING_TO_REPLACE_PER_ITERATION' 
  27.      ) AS 'Wednesday', 
  28.      ( 
  29.          SELECT SEC_TO_TIME(SUM(TIME_TO_SEC(s.EstimateTimeFinish)-TIME_TO_SEC(s.DateTimeCreated))) AS SecondsElapsed 
  30.          FROM StaffActivities s 
  31.          WHERE WEEKDAY(s.DateTimeCreated)=3 
  32.          AND YEARWEEK(s.DateTimeCreated)=YEARWEEK(NOW()) - $w_value 
  33.          AND s.StaffUserID='STRING_TO_REPLACE_PER_ITERATION' 
  34.      ) AS 'Thursday', 
  35.      ( 
  36.          SELECT SEC_TO_TIME(SUM(TIME_TO_SEC(s.EstimateTimeFinish)-TIME_TO_SEC(s.DateTimeCreated))) AS SecondsElapsed 
  37.          FROM StaffActivities s 
  38.          WHERE WEEKDAY(s.DateTimeCreated)=4 
  39.          AND YEARWEEK(s.DateTimeCreated)=YEARWEEK(NOW()) - $w_value 
  40.          AND s.StaffUserID='STRING_TO_REPLACE_PER_ITERATION' 
  41.      ) AS 'Friday', 
  42.      ( 
  43.          SELECT SEC_TO_TIME(SUM(TIME_TO_SEC(s.EstimateTimeFinish)-TIME_TO_SEC(s.DateTimeCreated))) AS SecondsElapsed 
  44.          FROM StaffActivities s 
  45.          WHERE WEEKDAY(s.DateTimeCreated)=5 
  46.          AND YEARWEEK(s.DateTimeCreated)=YEARWEEK(NOW()) - $w_value 
  47.          AND s.StaffUserID='STRING_TO_REPLACE_PER_ITERATION' 
  48.      ) AS 'Saturday', 
  49.      ( 
  50.          SELECT SEC_TO_TIME(SUM(TIME_TO_SEC(s.EstimateTimeFinish)-TIME_TO_SEC(s.DateTimeCreated))) AS SecondsElapsed 
  51.          FROM StaffActivities s 
  52.          WHERE WEEKDAY(s.DateTimeCreated)=6 
  53.          AND YEARWEEK(s.DateTimeCreated)=YEARWEEK(NOW()) - $w_value 
  54.          AND s.StaffUserID='STRING_TO_REPLACE_PER_ITERATION' 
  55.      ) AS 'Sunday', 
  56.      ( 
  57.          SELECT SEC_TO_TIME(SUM(TIME_TO_SEC(s.EstimateTimeFinish)-TIME_TO_SEC(s.DateTimeCreated))) AS SecondsElapsed 
  58.          FROM StaffActivities s 
  59.          WHERE YEARWEEK(s.DateTimeCreated)=YEARWEEK(NOW()) - $w_value 
  60.          AND s.StaffUserID='STRING_TO_REPLACE_PER_ITERATION' 
  61.      ) AS 'Total', 
  62.      ( 
  63.          SELECT ROUND((SUM(TIME_TO_SEC(s.EstimateTimeFinish)-TIME_TO_SEC(s.DateTimeCreated))/26640),2) AS SecondsElapsed 
  64.          FROM StaffActivities s 
  65.          WHERE YEARWEEK(s.DateTimeCreated)=YEARWEEK(NOW()) - $w_value 
  66.          AND s.StaffUserID='STRING_TO_REPLACE_PER_ITERATION' 
  67.      ) AS 'Total Days' 
  68.  FROM StaffActivities s 
  69.  WHERE s.StaffUserID='STRING_TO_REPLACE_PER_ITERATION' 
  70.  "
  71.   
  72.  // POST-NOTES: 
  73.  // 26640 is 7 hours 24 minutes (our working day)
  74.  // 4 days times 7:30 and 1 day of 7:00 will result in 37:00 total. 
  75.  // Our working week starts on Monday 00:00 and ends on Sunday 23:59. 

Putting it together
Your first query should loop through the users list, within the loop run the activities query and replace the string "STRING_TO_REPLACE_PER_ITERATION" with the User ID. Something like:
copyraw
$result=mysql_query($user_list_query);
while($row=mysql_fetch_assoc($result)) {
	$this_user_id=$row['UserID'];
	$this_user_full=$row['Fullname'];
	$this_user_query=str_replace("STRING_TO_REPLACE_PER_ITERATION", $this_user_id, $timesheet_query_per_user);
	$this_mon=$this_tue=$this_wed=$this_thu=$this_fri=$this_sat=$this_sun=$this_t=$this_t2="";


	$sub_result=mysql_query($this_user_query);
	while($sub_row=mysql_fetch_assoc($sub_result)) {
		$this_mon=$sub_row['Monday'];
		$this_tue=$sub_row['Tuesday'];
		$this_wed=$sub_row['Wednesday'];
		$this_thu=$sub_row['Thursday'];
		$this_fri=$sub_row['Friday'];
		$this_sat=$sub_row['Saturday'];
		$this_sun=$sub_row['Sunday'];
		$this_t=$sub_row['Total'];
		$this_t2=$sub_row['Total Days'];

		echo $this_user_full . "\t" . $this_mon . "\t" . $this_tue . "\t" . $this_wed . "\t" . $this_thu . "\t" . $this_fri . "\t" . $this_sat . "\t" . $this_sun . "\t" . $this_t . "\t" . $this_t2;

                ...do some more here ???...
        }
}
  1.  $result=mysql_query($user_list_query)
  2.  while($row=mysql_fetch_assoc($result)) { 
  3.      $this_user_id=$row['UserID']
  4.      $this_user_full=$row['Fullname']
  5.      $this_user_query=str_replace("STRING_TO_REPLACE_PER_ITERATION", $this_user_id, $timesheet_query_per_user)
  6.      $this_mon=$this_tue=$this_wed=$this_thu=$this_fri=$this_sat=$this_sun=$this_t=$this_t2=""
  7.   
  8.   
  9.      $sub_result=mysql_query($this_user_query)
  10.      while($sub_row=mysql_fetch_assoc($sub_result)) { 
  11.          $this_mon=$sub_row['Monday']
  12.          $this_tue=$sub_row['Tuesday']
  13.          $this_wed=$sub_row['Wednesday']
  14.          $this_thu=$sub_row['Thursday']
  15.          $this_fri=$sub_row['Friday']
  16.          $this_sat=$sub_row['Saturday']
  17.          $this_sun=$sub_row['Sunday']
  18.          $this_t=$sub_row['Total']
  19.          $this_t2=$sub_row['Total Days']
  20.   
  21.          echo $this_user_full . "\t" . $this_mon . "\t" . $this_tue . "\t" . $this_wed . "\t" . $this_thu . "\t" . $this_fri . "\t" . $this_sat . "\t" . $this_sun . "\t" . $this_t . "\t" . $this_t2
  22.   
  23.                  ...do some more here ???... 
  24.          } 
  25.  } 
Yields
copyraw
-- Note this data matches the brief as we've converted the seconds to time.
-- Using MySQL to convert rather than PHP so as not to have to deal with hour changes and the magical 23:00 hour.

-- 2nd Note: We use "WEEKDAY" instead of "DAYOFWEEK" as our working week starts on Monday (Mon=0, Tue=1, Sun=6).  

User		Mon	Tue	Wed	Thu	Fri	Sat	Sun	Total	Days
--------------- ------- ------- ------- ------- ------- ------- ------- ------- -----
Me		06:30	08:00	07:30	06:30	07:30	NULL	NULL    36:00   4.86
Myself		NULL	07:30	07:30	07:30	07:30	NULL	NULL    30:00   4.05
I		NULL	NULL	08:00	08:00	08:00	NULL	NULL    24:00   3.24
  1.  -- Note this data matches the brief as we've converted the seconds to time. 
  2.  -- Using MySQL to convert rather than PHP so as not to have to deal with hour changes and the magical 23:00 hour. 
  3.   
  4.  -- 2nd Note: We use "WEEKDAY" instead of "DAYOFWEEK" as our working week starts on Monday (Mon=0, Tue=1, Sun=6)
  5.   
  6.  User        Mon    Tue    Wed    Thu    Fri    Sat    Sun    Total    Days 
  7.  --------------- ------- ------- ------- ------- ------- ------- ------- ------- ----- 
  8.  Me        06:30    08:00    07:30    06:30    07:30    NULL    NULL    36:00   4.86 
  9.  Myself        NULL    07:30    07:30    07:30    07:30    NULL    NULL    30:00   4.05 
  10.  I        NULL    NULL    08:00    08:00    08:00    NULL    NULL    24:00   3.24 
Category: MySQL :: Article: 416

Credit where Credit is Due:


Feel free to copy, redistribute and share this information. All that we ask is that you attribute credit and possibly even a link back to this website as it really helps in our search engine rankings.

Disclaimer: The information on this website is provided without warranty and any content is merely the opinion of the author. Please try to test in development environments prior to adapting them to your production environments. The articles are written in good faith and, at the time of print, are working examples used in a commercial setting.

Thank you for visiting and, as always, we hope this website was of some use to you!

Kind Regards,

Joel Lipman
www.joellipman.com

Related Articles

Joes Revolver Map

Accreditation

Badge - Certified Zoho Creator Associate
Badge - Certified Zoho Creator Associate

Donate & Support

If you like my content, and would like to support this sharing site, feel free to donate using a method below:

Paypal:
Donate to Joel Lipman via PayPal

Bitcoin:
Donate to Joel Lipman with Bitcoin - Valid till 8 May 2022 3QnhmaBX7LQSRsC9hh6Je9rGQKEGNQNfPb
© 2022 Joel Lipman .com. All Rights Reserved.