Let’s take it step-by-step…
First, the entities you’re selecting are in the leave_request
table. So let’s start there:
SELECT leave_request.* FROM leave_request
Now, you need to know the data for the applied_by
column in the desired results. So you join the staff
table:
SELECT
applied_staff.name AS applied_by
FROM
leave_request
INNER JOIN staff AS applied_staff ON leave_request.staff_id = applied_staff.staff_id
(Note that I’m using aliases for the table names. This will come in handy later.)
Now you need to know applied_from
and applied_to
, which you already have available:
SELECT
applied_staff.name AS applied_by,
leave_request.applied_from,
leave_request.applied_to
FROM
leave_request
INNER JOIN staff AS applied_staff ON leave_request.staff_id = applied_staff.staff_id
Now you need to know approved_from
and approved_to
, which are in the leave_approval
table:
SELECT
applied_staff.name AS applied_by,
leave_request.applied_from,
leave_request.applied_to,
admin_approval.approved_from,
admin_approval.approved_to
FROM
leave_request
INNER JOIN staff AS applied_staff ON leave_request.staff_id = applied_staff.staff_id
INNER JOIN leave_approval AS admin_approval ON leave_request.request_id = admin_approval.request_id
Uh oh, now we have a problem. There’s a one-to-many relationship, so now we have duplicated leave requests in the results. We need to filter that down somehow. You don’t specify how, so I’m going to make a couple assumptions: You want to know the approved_from
and approved_to
of the “admin” approval AND there will only be ONE “admin” approval.
Let’s reflect those assumptions in the table joins:
SELECT
applied_staff.name AS applied_by,
leave_request.applied_from,
leave_request.applied_to,
admin_approval.approved_from,
admin_approval.approved_to
FROM
leave_request
INNER JOIN staff AS applied_staff ON leave_request.staff_id = applied_staff.staff_id
INNER JOIN leave_approval AS admin_approval ON leave_request.request_id = admin_approval.request_id
INNER JOIN staff AS approved_staff ON admin_approval.approved_by = approved_staff.staff_id
INNER JOIN group AS approved_staff_group on approved_staff.group_id = approved_staff_group.group_id
WHERE
approved_staff_group.group_name="admin"
That should be better. Note that the table aliasing came in handy here because we now have two instances of the staff
table for two different purposes in the same query. So we needed to distinguish them. (Keep in mind that I’m flying blind here and can’t actually test any of this. So correct me if there are any problems encountered along the way. I’m also free-handing this code because I don’t have MySQL handy, so let me know if there are syntax errors as well.)
Now let’s add the approved_admin
field to the results, which is already available:
SELECT
applied_staff.name AS applied_by,
leave_request.applied_from,
leave_request.applied_to,
admin_approval.approved_from,
admin_approval.approved_to,
approved_staff.name AS approved_admin
FROM
leave_request
INNER JOIN staff AS applied_staff ON leave_request.staff_id = applied_staff.staff_id
INNER JOIN leave_approval AS admin_approval ON leave_request.request_id = admin_approval.request_id
INNER JOIN staff AS approved_staff ON admin_approval.approved_by = approved_staff.staff_id
INNER JOIN group AS approved_staff_group on approved_staff.group_id = approved_staff_group.group_id
WHERE
approved_staff_group.group_name="admin"
Finally, we need to know the approved_hr
. And null
is allowed? We’re going to use a different join for this one, then. I’m also making similar assumptions to those above. Let’s try this:
SELECT
applied_staff.name AS applied_by,
leave_request.applied_from,
leave_request.applied_to,
admin_approval.approved_from,
admin_approval.approved_to,
approved_staff.name AS approved_admin,
hr_staff.name AS approved_hr
FROM
leave_request
INNER JOIN staff AS applied_staff ON leave_request.staff_id = applied_staff.staff_id
INNER JOIN leave_approval AS admin_approval ON leave_request.request_id = admin_approval.request_id
INNER JOIN staff AS approved_staff ON admin_approval.approved_by = approved_staff.staff_id
INNER JOIN group AS approved_staff_group on approved_staff.group_id = approved_staff_group.group_id
LEFT OUTER JOIN leave_approval AS hr_approval ON leave_request.request_id = hr_approval.request_id
LEFT OUTER JOIN staff AS hr_staff ON hr_approval.approved_by = hr_staff.staff_id
LEFT OUTER JOIN group AS hr_staff_group ON hr_staff.group_id = hr_staff_group.group_id
WHERE
approved_staff_group.group_name="admin"
AND hr_staff_group.group_name="HR"
I’m not entirely sure about those latter LEFT OUTER JOIN
s. The first one is definitely going to need to be a join that allows for null
values, but I’m not sure how the query engine handles joins beyond that. I’d prefer that they be INNER JOIN
s within the scope of the initial LEFT OUTER JOIN
. But I guess all of that really also depends on the integrity of the data, which I can’t guarantee.
It’s also worth noting that you claim to want "Jack"
as output when the value is "jack"
. I didn’t do any string manipulation in this code to make that happen. If the value should be capitalized in the data, then capitalize it in the data.
Again, I can’t guarantee this code. But as a walk-through it should get you moving in the right direction. As I mentioned in a comment on the question, I really recommend picking up a book on MySQL if you’re going to be writing MySQL code.
Edit: One recommendation I can give is to the structure of the data itself. Specifically that leave_approval
table feels a bit messy, and it’s that table alone which is causing the confusion. I have a couple recommendations:
- Add an
approval_type
to theleave_approval
table. At the very least this would indicate if it’s an admin approval, an HR approval, or any other kind of approval. (Are there even other kinds? Will there ever be?) Then you could also userequest_id
andapproval_type
as a combined primary key, or at least a combined unique constraint, to enforce better data integrity and prevent duplicate approvals. - If there are only two kinds of approvals and that’s probably not going to change, reflect them both in the
leave_approval
table. Have one set of columns foradmin_approval_*
and one set forhr_approval_*
. (Each set would include thestaff_id
and relevant dates for the approval.) Thenrequest_id
itself could be a primary key onleave_approval
making it one-to-one withleave_request
. This would dramatically simplify the relational data, essentially turning aleave_approval
record into an optional set of additional information for aleave_request
record. The joins would become much simpler and the data would express itself much more clearly.
solved joins in MySQL for leave management in PHP and MySQL [closed]