Fetch active task of a person with joins - mysql

I have a person table and a task table. Though a task can be done by multiple people, so in between there is a 'roster' table:
person
task_roster
task
I'm looking for a query that returns each person and their current active task. If they have no active task, it should simply return NULL. I've been messing around with the following:
SELECT *
FROM person
LEFT OUTER JOIN task_roster ON (task_roster.person = person.id)
LEFT OUTER JOIN task on (task.id = task_roster.task AND task.status = 1)
ORDER BY person.name
The problem I'm running in too is that this query returns old roster entries, so I get the same person back multiple times, for each previous task they performed. I've also tried adding a Group By on person.name, though then I don't get any of the active duties, just NULL values.
The restult I'm looking for would be like this:
+----------------+-----------+----------------+
| person.name | task.id | task.name |
+----------------+-----------+----------------+
| David Harris | 5 | Maintenance |
| Karen Simmons | 2 | Transport |
| Linda Foster | 5 | Maintenance |
| Michael King | NULL | NULL |
+----------------+-----------+----------------+
David and Linda are both working on the same task. The roster table is nothing more then a link between person.id and task.id

I wonder if this would work:
SELECT person.*,task.status
FROM person
LEFT OUTER JOIN task_roster ON (task_roster.person = person.id)
LEFT OUTER JOIN task on task.id = task_roster.task
WHERE task.status=1
ORDER BY person.name

The reason why you are getting also the old task is because of this join statement:
...
task_roster ON (task_roster.person = person.id)
LEFT OUTER JOIN task
...
basically it displays all the records from table task_roster. There may be other solutions on this but my solution below uses a subquery that gets only person with specific active task. The result of the subquery is then joined back on table person using LEFT JOIN so every record on table person will be shown on the list whether it has a matching record on the subquery or none.
SELECT a.name AS PersonName,
b.task AS TaskID,
b.name AS TaskName
FROM person a
LEFT JOIN
(
SELECT a.person, a.task, b.name
FROM task_roster a
INNER JOIN task b
ON a.task = b.id
WHERE b.status = 1
) b ON a.id = b.person

Related

Mysql count the different id's of the foreign key

I have the following tables:
jobs:
-------------------------------------------------------
| id | title | slug |
-------------------------------------------------------
employments:
-------------------------------------------------------
| id | job_type|
-------------------------------------------------------
applications:
-------------------------------------------------------
| id | job_opening_id| application_state_id|
-------------------------------------------------------
application_states
-------------------------------------------------------
| id | name|
-------------------------------------------------------
I want to create a query that counts the different application_state_id's
----------------------------------------------------------------------------
| j.title| j.slug| e.job_type | candidates | hired
----------------------------------------------------------------------------
This is the query that i have at the moment:
SELECT
j.title,
j.slug,
e.job_type,
count(a1.application_state_id) as candidates,
count(a2.application_state_id) as hired
FROM
jobs AS j
INNER JOIN employments AS e ON j.employment_id = e.id
LEFT JOIN applications AS a1 ON a1.job_opening_id = job_openings.id
LEFT JOIN application_states AS as ON as.id = a1.application_state_id
LEFT JOIN applications AS a2 ON a2.job_opening_id = j.id AND a2.application_state_id = 1
GROUP BY
a1.application_state_id,
a2.application_state_id,
j.id,
j.title,
j.slug
I thought i could create 2 joins and set the application_state_id, but all that does is count records double.
What do i need to change in this query? I hope someone can help me.
You did not provide sample data, but as I see from your code
you are joining the table applications twice,
so by the 1st to get the total number of candidates
and by the 2nd to get the total number of hired candidates.
I think you can drop the 2nd join and do conditional counting to get the total number of hired candidates.
Also:
the select statement must include the columns that you group by and any aggregated columns
and I don't see why you need to join to the application_states table.
Try this:
SELECT
j.title,
j.slug,
e.job_type,
count(a.application_state_id) as candidates,
sum(case when a.application_state_id = 1 then 1 else 0 end) as hired
FROM
jobs AS j INNER JOIN employments AS e ON j.employment_id = e.id
LEFT JOIN applications AS a ON a.job_opening_id = job_openings.id
GROUP BY
j.title,
j.slug,
e.job_type

Removing an ID from a NOT IN

I'm trying to write a query to calculate the total charged on a per client basis, where a client has a registered property in one table, where they have not been invoiced in another, but I am not sure how to write the 'Not In' portion of the query so that it does not have to reference a specific ID.
The query listed below is part of a larger query to calculate overall totals, but I am struggling with this particular piece.
I have tried to modify the 'Not In' portion of the query to remove the reference to a specific ID and do a Group By the client_id, but this did not return any results. I also tried a GROUP_CONCAT, but I'm not sure I implemented that properly.
I'm not sure if a) what I'm trying to do is possible, or b) if there's a much better way to go about doing what I'm trying to accomplish, but I feel like if I could write this in another subquery, I'd be in much better shape, but I can't wrap my head around how to write it or even another way to approach the problem.
SELECT
clients.client_id,
clients.first_name,
clients.last_name,
SUM(total_charged) AS total_charged
FROM
clients
LEFT JOIN
(SELECT
clients.client_id,
route_property_reg.route_property_reg_id,
route_property_reg.rate_charged AS total_charged
FROM
clients
LEFT JOIN properties ON clients.client_id = properties.client_id
LEFT JOIN route_property_reg ON properties.property_id = route_property_reg.property_id
WHERE
route_property_reg.route_property_reg_id NOT IN (
SELECT DISTINCT
invoice_lines.route_property_reg_id
FROM
clients
LEFT JOIN invoices ON clients.client_id = invoices.client_id
LEFT JOIN invoice_lines ON invoices.invoice_id = invoice_lines.invoice_id
WHERE
clients.client_id = 1)
GROUP BY clients.client_id)
AS charged ON clients.client_id = charged.client_id
GROUP BY clients.client_id
SQL Fiddle
An alternative Fiddle with another attempt but similar results: SQL Fiddle
Expected/Anticipated Output would be:
| client_id | first_name | last_name | total_charged |
|-----------|------------|-----------|---------------|
| 1 | John | Doe | 200 | - (route-properties # 1 and 4)
| 2 | Jane | Doe | 200 | - (route-property #5)
| 3 | John | Smith | (null) |
| 4 | Jane | Smith | (null) |
After a lot of guess and check, the following works for me:
SELECT
clients.client_id,
clients.first_name,
COALESCE(SUM(total_charged), 0) AS total_charged
FROM
clients
LEFT JOIN (SELECT
c.client_id,
a.route_property_reg_id,
COALESCE(SUM(ROUND((a.rate_charged * (1 + (tax_types.rate / 100))), 2)), 0) AS total_charged
FROM
route_property_reg a
LEFT JOIN properties ON a.property_id = properties.property_id
LEFT JOIN clients c ON properties.client_id = c.client_id
LEFT JOIN tax_types ON a.tax_rate_charged = tax_types.tax_type_id
WHERE
NOT EXISTS( SELECT
NULL
FROM
invoice_lines b
LEFT JOIN invoices ON b.invoice_id = invoices.invoice_id
LEFT JOIN clients d ON invoices.client_id = d.client_id
WHERE
b.route_property_reg_id = a.route_property_reg_id
AND d.client_id = c.client_id)
GROUP BY client_id) AS charged ON clients.client_id = charged.client_id
GROUP BY clients.client_id

Mysql select between two table without limiting if record appear on the joined table

I have been trying to figure out how to select data related to one id between to tables without limit it to the joined table. I tried using UNION, Inner join, JOIN, but it limit me to show records that are only in both tables. By example:
Table 1 (users)
id | name | register
1 | John | 2014-03-01
2 | Kate | 2014-03-02
etc..
Table 2 (birthdays by example)
id | user | birthday
1 | 1 | 1989-09-09
Note that kate dont have a record on the birthdays table, if i do:
SELECT U.id, name, register, B.birthday FROM users as U INNER JOIN birthday as B ON B.user = U.id
it will only shows JOHN data, i would like to select all my users and if the record do not exist on the joined table, still be able to select all my users, sort of:
id | name | register | birthday
1 | John | 2014-03-01 | 1989-09-09
2 | kate | 2014-03-02 | null or ''
3
4
etc.
Sorry if its a stupid question but i dont find the light on this one. I would appreciate the help.
Regards
You need a LEFT OUTER JOIN instead of the plain JOIN (also known as INNER JOIN), like this:
SELECT U.id, name, register, B.birthday
FROM users as U
LEFT JOIN birthday as B
ON B.user = U.id
A LEFT JOIN between users and birthday tables will contain all records of the "left" table (users), even if the join-condition does not find any matching record in the "right" table (birthday).
This excellent article on The Code Project will help you a lot: Visual Representation of SQL Joins.
Summary of all JOIN types:
Note: Mysql does not support FULL OUTER JOIN but it can be emulated. Useful articles:
https://stackoverflow.com/a/4796911
http://www.sql-tutorial.ru/en/book_full_join_and_mysql.html
http://www.xaprb.com/blog/2006/05/26/how-to-write-full-outer-join-in-mysql/
Use left outer join instead of inner join..
SELECT U.id, name, register, B.birthday
FROM users as U left join birthday as B ON B.user = U.id

Problem with one of my LEFT JOIN and SUM the result of it

So I got a question about LEFT JOIN, this code returns different values for totalPoints depending on if the user got the group or not. (if user don't got group or event it returns the correct value)
I just want to grasp how to get the LEFT JOIN flow_has_vote ON flow_has_vote.flow_id=flows.id to work every time. I did a solution before with three query's, one that gets the group and event rule, one that checks if the user got the group or event considering the security and one to get the flow...
And I guess I could solve this by having two query's, one that gets the group and event rules and also check if the user got the group and event and then one that gets the flow depending on the user should have access to it.
Right now I'm getting every information needed in ONE query and then checking with IF statements if it should be printed or not...
So, my question is, is it possible to get the SUM(flow_has_vote.points) AS totalPoints to work this way? And do you know how?
And also I'm a bit curios, is one query the best way to work with this? Would it be justified to use two when you take into account performance?
SELECT
flows.id AS flowId,
flows.security,
SUM(flow__has__vote.points) AS totalPoints,
users.id AS userId,
users.alias,
flows.event_id AS eventId,
events.group_id AS groupId,
events.membershipRules AS eMR,
groups.membershipRules AS gMR,
user__has__group.permission AS userHasGroup,
user__has__event.permission AS userHasEvent
FROM
users,
events LEFT JOIN user__has__event ON user__has__event.user_id = '.$userId.',
groups LEFT JOIN user__has__group ON user__has__group.user_id = '.$userId.',
flows LEFT JOIN flow__has__vote ON flow__has__vote.flow_id=flows.id
WHERE
flows.user_id = users.id AND
events.id = flows.event_id AND
groups.id = events.group_id AND
flows.id='.$flowId
And if you wonder what the SQL-statement is doing, getting the information for the flow(post), the information about the event and group that the flow is in, checking the user access to the group and event and also getting all the votes for the flow...
This is how the tables looks like...
FLOWS id,security,event_id,user_id
USERS id, alias
EVENTS id, name group_id, membershipRules
GROUPS id, name, membershipRules
USER__HAS__GROUP user_id,group_id,permission
USER__HAS__EVENT user_id,event_id,permission
FLOW__HAS__VOTE flow_id,user_id,points
This is the result I wish for...
+--------+----------+-------------+--------+--------+---------+---------+-----+-----+--------------+--------------+
| flowId | security | totalPoints | userId | alias | eventId | groupId | eMR | gMR | userHasGroup | userHasEvent |
+--------+----------+-------------+--------+--------+---------+---------+-----+-----+--------------+--------------+
| 1 | 2 | 1337 | 5 | Pontus | 15 | 2 | 2 | 2 | 4 | 4 |
+--------+----------+-------------+--------+--------+---------+---------+-----+-----+--------------+--------------+
and one more example...
+--------+----------+-------------+--------+--------+---------+---------+-----+-----+--------------+--------------+
| flowId | security | totalPoints | userId | alias | eventId | groupId | eMR | gMR | userHasGroup | userHasEvent |
+--------+----------+-------------+--------+--------+---------+---------+-----+-----+--------------+--------------+
| 1 | 2 | 1337 | 6 | Kezia | 15 | 2 | 2 | 2 | null | null |
+--------+----------+-------------+--------+--------+---------+---------+-----+-----+--------------+--------------+
Enjoy your life ~ Pontus
So, basically the main point (IMHO) is not to include conditions on tables you LEFT JOINed in the WHERE clause, since this makes the LEFT JOIN behave like an INNER JOIN.
Start with trying this query (although I am sure you will have to make adjustments as I am not sure exactly what you want as a result, more about this later):
SELECT
flows.id AS flowId,
flows.security,
SUM(flow__has__vote.points) AS totalPoints,
users.id AS userId,
users.alias,
flows.event_id AS eventId,
events.group_id AS groupId,
events.membershipRules AS eMR,
groups.membershipRules AS gMR,
user__has__group.permission AS userHasGroup,
user__has__event.permission AS userHasEvent
FROM users,
LEFT JOIN user__has__event
ON user__has__event.user_id = users.id,
LEFT JOIN events
ON user__has__event.event_id = events.id
LEFT JOIN user__has__group
ON user__has__group.user_id = users.id,
LEFT JOIN groups
ON user__has__group.group_id = groups.id
AND groups.id = events.group_id
LEFT JOIN flows
ON flows.user_id = users.id
AND events.id = flows.event_id
AND flows.id='.$flowId'
LEFT JOIN flow__has__vote
ON flow__has__vote.flow_id = flows.id
WHERE users.id = '.$userId.'
GROUP BY users.id
Here, I LEFT JOINed everything to the user, and also grouped by the user. I have a feeling you will want to add columns to the group by (flows.id?, events.id?)
Also, you may want to turn some of the LEFT JOINs to JOIN, so you will get only users who have a 'flow', for example.

Combine Multiple Query Results in MySQL (by column)

I have 4 different queries and each of them return individual unique set of Results.
I need to combine the Query Results with using a single query.
my sample queries are:
1. select cls.* from (calls as cls inner join calls_users as clsusr on cls.id=clsusr.call_id) inner join users as usr on usr.id=cls.assigned_user_id where cls.assigned_user_id='seed_max_id'
2. select mtn.* from (meetings as mtn inner join meetings_users as mtnusr on mtn.id=mtnusr.meeting_id) inner join users as usr on usr.id=mtn.assigned_user_id where mtn.assigned_user_id='seed_max_id'
3. select tsk.* from tasks as tsk inner join users as usr on usr.id=tsk.assigned_user_id where tsk.assigned_user_id='seed_max_id'
4. select nts.* from (notes as nts inner join accounts as acnts on acnts.id=nts.parent_id) inner join users as usr on usr.id=acnts.assigned_user_id where acnts.assigned_user_id='seed_max_id'
I tried the following way, but it didn't work
Combine: SELECT tbl1.*, tbl2.*
from (select cls.* from (calls as cls inner join calls_users as clsusr on cls.id=clsusr.call_id) inner join users as usr on usr.id=cls.assigned_user_id where cls.assigned_user_id='seed_max_id') as tbl1
left outer join
(select mtn.* from (meetings as mtn inner join meetings_users as mtnusr on mtn.id=mtnusr.meeting_id) inner join users as usr on usr.id=mtn.assigned_user_id where mtn.assigned_user_id='seed_max_id') as tbl2
using(assigned_user_id)
i also tried right outer join and other inner joins
I am really stuck, if any one know the solution then please help.
I need the similar result like How can I join two tables with different number of rows in MySQL?.
Data Sample:
From Query 1:
+-------------------------------------------+------------------+-
| Call Name | Call Description |
+-------------------------------------------+------------------+-
| Discuss Review Process | NULL |
| Get More information on the proposed deal | NULL |
| Left a message | NULL |
| Discuss Review Process | NULL |
+-------------------------------------------+------------------+
From Query 2:
+-----------------------+-----------------------------------------------------------
| Meeting Name | Meeting Description
+-----------------------+-----------------------------------------------------------
| Review needs | Meeting to discuss project plan and hash out the details o
| Initial discussion | Meeting to discuss project plan and hash out the details o
| Demo | Meeting to discuss project plan and hash out the details o
| Discuss pricing | Meeting to discuss project plan and hash out the details o
| Review needs | Meeting to discuss project plan and hash out the details o
+-----------------------+-----------------------------------------------------------
i need to combine the columns like the following:
+-------------------------------------------+------------------+-------------------+-------------------+
| Call Name | Call Description |Meeting Name |Meeting Description|
+-------------------------------------------+------------------+-------------------+-------------------+
| Discuss Review Process | NULL |Review needs |Meeting to discuss |
| Get More information on the proposed deal | NULL |Initial discussion |Meeting to discuss |
| Left a message | NULL |Demo |Meeting to discuss |
| NULL | NULL |Discuss pricing |Meeting to discuss |
| NULL | NULL |Review needs |Meeting to discuss |
+-------------------------------------------+------------------+-------------------+-------------------+
The best you can do is a UNION or UNION ALL but this requires them to have the same type and number of columns. For example:
SELECT 'Customer' AS type, id, name FROM customer
UNION ALL
SELECT 'Supplier', id, name FROM supplier
UNION ALL
SELECT 'Employee', id, full_name FROM employee
The column names don't have to match. The aliases from the first part will be used for the rest.
I'll also add that instead of:
select cls.* from (calls as cls inner join calls_users as clsusr on cls.id=clsusr.call_id) inner join users as usr on usr.id=cls.assigned_user_id where cls.assigned_user_id='seed_max_id'
you should remove the unnecessary subquery and just do:
SELECT c.*
FROM calls c
JOIN calls_users cu ONc.id = cu.call_id
WHERE c.assigned_user_id = 'seed_max_id'
There's no need for the extra complexity and the above is eminently more readable.
I assume you want your example to return a single row combining the corresponding entries from all these tables. Try this and tell us if it worked:
select * from users as usr
left outer join (calls as cls
inner join calls_users as clsusr
on cls.id = clsusr.call_id)
on usr.id = cls.assigned_user_id
left outer join (meetings as mtn
inner join meetings_users as mtnusr
on mtn.id = mtnusr.meeting_id)
on usr.id = mtn.assigned_user_id
left outer join tasks as tsk
on usr.id = tsk.assigned_user_id
left outer join (notes as nts
inner join accounts as acnts
on acnts.id=nts.parent_id)
on usr.id = acnts.assigned_user_id
where user.id = 'seed_max_id'