I am programming an office schedule.
I have a table users in which each user has a regular schedule hreg that is monday to friday.
I have a table hleave in which I enter a leave code for the dates a user is out of office.
Table hleave:
hleave_id user dateStart dateEnd leaveCode int(11)PK int(11)FK date date int(11)FK 1 5 2019-02-11 2019-02-13 1 2 1 2019-02-28 2019-02-28 1 3 3 2019-02-26 2019-02-28 2
Table users:
user_id firstName lastName link_team_id link_hreg_id int(11)PK varchar varchar int(11)FK int(11)FK 1 Bob Smith 1 1 2 Alice Fraser 1 1 3 Jenny Summers 1 1 4 Carl Raisman 1 1 5 Roger Wayne 1 1
Table teams:
team_id teamName int(11)PK varchar 1 team 1 2 team 2 3 team 3
Table hreg:
hreg_id weekStart weekEnd int(11)PK int(11) int(11) 1 1 5 2 1 4 3 2 5
Table leave_codes:
code_id codeName int(11)PK varchar 1 A 2 B 3 C 4 D
If there is an hleave entry with a date range that either starts this week, ends this week or starts before this week and ends after this week, I want to display the leave code.
If not, I want to display the user’s regular schedule “working”. For example:
Monday Tuesday Wednesday Thursday Friday BOB working working working A working ALICE working working working working working JENNY working B B B working CARL working working working working working
Here is my query for TEAM #1 (I replaced the variables $monday and $friday with real dates for clarity) :
SELECT
users.firstName,
users.lastName,
users.link_team_id,
users.link_hreg_id,
hreg.weekStart,
hreg.weekEnd,
leave_codes.codeName,
hleave.dateStart,
hleave.dateEnd
FROM users
LEFT JOIN hleave ON hleave.user = users.user_id
JOIN teams ON users.link_team_id = teams.team_id
JOIN hreg ON users.link_hreg_id = hreg.hreg_id
LEFT JOIN leave_codes ON hleave.leaveCode = leave_codes.code_id
WHERE
(CASE
WHEN (hleave.dateStart BETWEEN '2019-02-25' AND '2019-03-01')
THEN (users.link_team_id = 1)
WHEN (hleave.dateEnd BETWEEN '2019-02-25' AND '2019-03-01')
THEN (users.link_team_id = 1)
WHEN (hleave.dateStart < '2019-02-25' AND hleave.dateEnd > '2019-03-01')
THEN (users.link_team_id = 1)
WHEN (hleave.dateStart IS NULL AND hleave.dateEnd IS NULL)
THEN (users.link_team_id = 1)
END)
Results:
firstName lastName link_team_id link_hreg_id weekStart weekEnd codeName dateStart dateEnd Bob Smith 1 1 1 5 A 2019-02-28 2019-02-28 Alice Fraser 1 1 1 5 NULL NULL NULL Jenny Summers 1 1 1 5 B 2019-02-26 2019-02-28 Carl Raisman 1 1 1 5 NULL NULL NULL
This query works. I get the codeName, dateStart and dateEnd for a user if it happens this week. If dateStart and dateEnd are NULL, I still get the user firstName, lastName and hreg so I can display them on the website as working.
So far so good, but I have a problem. When dateStart < '2019-02-25' and dateEnd < '2019-03-01' (i.e. if the leave happened before this week) I get nothing for this user since the date range validates as FALSE and it is not NULL either. For instance, if Roger had a leave on 2019-02-11 to 2019-02-13, Roger is not included in the results set.
Same thing goes for a date range in the future : dateStart > '2019-02-25' and dateEnd> '2019-03-01'.
What would be the best way to achieve this? I tried CASE WHEN in the SELECT clause to pin poit the dates I want to select but the result is the same. I either need to select differently or to turn entries validating as FALSE to NULL?
Advertisement
Answer
As I noted in my comment, it appears that you’re making the query a bit more complex than needed.
The main issue is the CASE/WHEN in your WHERE criteria, limiting the users to those matching in the CASE/WHEN, Since Roger was on leave, but did not match the specified dates in the WHERE criteria, he was excluded.
As you appear to only be interested in a specific team, the user team can be filtered directly in your main query. Presuming hleave only contains an entry when the user is on leave for the date range queried, the condition can be moved from your WHERE to the LEFT JOIN, displaying NULL when the user is not on leave.
SELECT
users.firstName,
users.lastName,
users.link_team_id,
users.link_hreg_id,
hreg.weekStart,
hreg.weekEnd,
leave_codes.codeName,
hleave.dateStart,
hleave.dateEnd
FROM users
JOIN teams
ON users.link_team_id = teams.team_id
JOIN hreg
ON users.link_hreg_id = hreg.hreg_id
LEFT JOIN hleave
ON hleave.user = users.user_id
AND (
hleave.dateStart BETWEEN '2019-02-25' AND '2019-03-01' #leave starts this week
OR
hleave.dateEnd BETWEEN '2019-02-25' AND '2019-03-01' #leave ends this week
OR
(hleave.dateStart < '2019-02-25' AND hleave.dateEnd > '2019-03-01') #leave started before this week and continues after
)
LEFT JOIN leave_codes
ON hleave.leaveCode = leave_codes.code_id
WHERE users.link_team_id = 1
Results in:
<table>
<thead>
<tr>
<th class="col0">firstName</th>
<th class="col1">lastName</th>
<th class="col2">link_team_id</th>
<th class="col3">link_hreg_id</th>
<th class="col4">weekStart</th>
<th class="col5">weekEnd</th>
<th class="col6">codeName</th>
<th class="col7">dateStart</th>
<th class="col8">dateEnd</th>
</tr>
</thead>
<tbody>
<tr>
<td class="col0">Bob</td>
<td class="col1">Smith</td>
<td class="col2">1</td>
<td class="col3">1</td>
<td class="col4">1</td>
<td class="col5">5</td>
<td class="col6">A</td>
<td class="col7">2019-02-28</td>
<td class="col8">2019-02-28</td>
</tr>
<tr>
<td class="col0">Jenny</td>
<td class="col1">Summers</td>
<td class="col2">1</td>
<td class="col3">1</td>
<td class="col4">1</td>
<td class="col5">5</td>
<td class="col6">B</td>
<td class="col7">2019-02-26</td>
<td class="col8">2019-02-28</td>
</tr>
<tr>
<td class="col0">Alice</td>
<td class="col1">Fraser</td>
<td class="col2">1</td>
<td class="col3">1</td>
<td class="col4">1</td>
<td class="col5">5</td>
<td class="col6"></td>
<td class="col7"></td>
<td class="col8"></td>
</tr>
<tr>
<td class="col0">Carl</td>
<td class="col1">Raisman</td>
<td class="col2">1</td>
<td class="col3">1</td>
<td class="col4">1</td>
<td class="col5">5</td>
<td class="col6"></td>
<td class="col7"></td>
<td class="col8"></td>
</tr>
<tr>
<td class="col0">Roger</td>
<td class="col1">Wayne</td>
<td class="col2">1</td>
<td class="col3">1</td>
<td class="col4">1</td>
<td class="col5">5</td>
<td class="col6"></td>
<td class="col7"></td>
<td class="col8"></td>
</tr>
</tbody>
</table>