sql to find number of attendees (joined) of an event - mysql

I'm trying to find number of attendees who have actually joined the sessions of the event. I've 5 tables.
event -> day -> session
attendee
session_attendee
Schema
+------------------------------+
| attendee |
+------------------------------+
| id username password |
| 1 user1 test |
| 2 user2 test |
| ------ |
| event |
| id name |
| 1 event 1 |
| 2 event 2 |
| ----- |
| day |
| id date event_id |
| 1 '2015-06-01' 1 |
| 2 '2015-06-02' 1 |
| 3 '2015-07-01' 2 |
| 4 '2015-07-02' 2 |
| ------ |
| session |
| id name day_id |
| 1 session a 1 |
| 2 session b 1 |
| 3 session c 2 |
| 4 session d 2 |
| ------ |
| session_attendee |
| id session_id attendee_id |
| 1 1 1 |
| 2 2 1 |
| 3 1 2 |
| 4 2 2 |
+------------------------------+
Expectation
+-----------------------------+
| id name attendees |
+-----------------------------+
| 1 'event 1' 2 |
| 2 'event 2' 0/null etc. |
+-----------------------------+
Subquery
SQL query I've tried using Joins but it's returning more than one record (i.e. I can't use it as a subquery)
SELECT count(*) FROM event
INNER JOIN day ON event.id = day.event_id
INNER JOIN session ON day.id = session.day_id
INNER JOIN session_attendee ON session.id = session_attendee.session_id
WHERE event.id = 1
GROUP BY attendee_id
Parent Query
with above subquery substituted
SELECT id, name,
(SELECT COUNT(*) FROM day WHERE day.event_id = event.id) AS days,
(SELECT COUNT(*) FROM event
INNER JOIN day ON event.id = day.event_id
INNER JOIN session ON day.id = session.day_id
INNER JOIN session_attendee ON session.id = session_attendee.session_id
GROUP BY session_attendee.attendee_id) AS attendees
FROM event;
SQL Fiddle
http://sqlfiddle.com/#!9/72ffc
More Work:
SELECT event.id, event.name,
(SELECT count(*) FROM event e INNER JOIN day AS d ON e.id = d.event_id WHERE e.id = event.id) AS total_days,
day.date, session.name, session_attendee.attendee_id
FROM event
LEFT JOIN day ON event.id = day.event_id
LEFT JOIN session ON day.id = session.day_id
LEFT JOIN session_attendee ON session.id = session_attendee.session_id
ORDER BY event.id;
-- COUNT(distinct attendee_id) AS attendees
As soon as I enter COUNT(distinct attendee_id), my result set gets reduced to one row, i.e. I don't get to see records for other events.

Your first query looks basically right with one exception: if you want information for an event, you should aggregate by the event id, not the attendee id:
SELECT e.id, e.name, count(*)
FROM event e INNER JOIN
day d
ON e.id = d.event_id INNER JOIN
session s
ON d.id = s.day_id INNER JOIN
session_attendee sa
ON s.id = sa.session_id
GROUP BY e.id;
Of course, if you want information for only one event, you can replace the GROUP BY with:
WHERE e.id = 1
EDIT:
You want a LEFT JOIN to keep all the events:
SELECT e.id, e.name, count(distinct sa.attendee_id)
FROM event e LEFT JOIN
day d
ON e.id = d.event_id LEFT JOIN
session s
ON d.id = s.day_id LEFT JOIN
session_attendee sa
ON s.id = sa.session_id
GROUP BY e.id;

Try this
SELECT event.id, name,
(SELECT COUNT(*) FROM day WHERE day.event_id = event.id) AS days,
b.number_of_attendee
FROM event
left join (
SELECT a.id, count(*) as number_of_attendee from
(
SELECT event.id, attendee_id FROM event
INNER JOIN day ON event.id = day.event_id
INNER JOIN session ON day.id = session.day_id
INNER JOIN session_attendee ON session.id = session_attendee.session_id
group by event.id, attendee_id
) as a
group by a.id) as b
on b.id = event.id
which has the result like
id name days number_of_attendee
1 Event 1 2 2
2 Event 2 2 (null)

Related

Multiple joins in mysql tables with union

In mysql, I am having an issue trying to get the right data. I think I have to use union to get all the results from both tables, but not sure how to do it.
Description of tables:
Order holds order numbers
Employee holds the employee
Zone holds the zones names
Actual time has the zone id, the order id and the amount of hours it should take to deliver
Deliver details contains the employee id, the zone the employee delivered and the amount of hours it took to deliver
Order
| id | number |
|----|--------|
| 1 | 0001 |
employees
| id | name |
|----|------|
| 1 | Jon |
zones
| id | name |
|----|-------|
| 1 | ZoneA |
| 2 | ZoneB |
actual_times
| id | zone_id | eta_hours | order_id |
|----|---------|-----------|----------|
| 1 | 1 | 5 | 1 |
| 2 | 2 | 4 | 1 |
deliver_details
| id | order_id | employee_id | zone_id | hours |
|----|----------|-------------|---------|-------|
| 1 | 1 | 1 | 1 | 3 |
| 2 | 1 | 1 | 1 | 1 |
What I am hoping to get is the zone name, the amount of hours it takes to deliver and the sum of hours the employee took deliver. If the employee did not deliver to that zone then show 0
Expected output
| zone_name | hours | eta_hours | employee_name |
|-----------|-------|-----------|---------------|
| ZoneA | 4 | 5 | Jon |
| ZoneB | 0 | 4 | Jon |
I tried making a union all on the actual time but I am not getting it right.
This is something I tried (note that this was just to get the right zones with deliver times and actual times).
SELECT deliver_details.zone_id, actual_times.zone_id, zones.zone_name FROM actual_times
RIGHT JOIN deliver_details ON actual_times.order_id = deliver_details.order_id
INNER JOIN zones ON zones.id = deliver_details.zone_id
WHERE deliver_details.order_id = 1
GROUP BY deliver_details.zone_id
UNION ALL
SELECT deliver_details.zone_id, actual_times.zone_id, zones.zone_name FROM actual_times
LEFT JOIN deliver_details ON actual_times.order_id = deliver_details.order_id
INNER JOIN zones ON zones.id = actual_times.zone_id
WHERE actual_times.order_id = 1
group by actual_times.zone_id
I am pretty much trying to get all of this in one query. Is there a way to do this?
Please note that this is a simplification to a more complex problem I am having. If you need more explanation or something does not make sense, please let me know.
No need to use UNION.
Start from table employees, then CROSS JOIN with zones and actual_times to get a simple cartesian products. Then search the deliver_details for deliveries performed by each employee on each zone ; use a LEFT JOIN for that. If an epmplyee did not deliver on a given zone, use COALESCE to return 0 instead of NULL.
Query :
select
z.name,
coalesce(sum(dd.hours), 0),
at.eta_hours,
e.name
from
employees e
cross join zones z
inner join actual_times at on at.zone_id = z.id
left join deliver_details dd on dd.employee_id = e.id and dd.id = at.zone_id
group by
z.name, at.eta_hours, e.name
I came up with a simillar solution to GMB, but using UNION to get rows with 0 hours...:
SELECT z.name, sum(dd.hours), at.eta_hours, e.name
FROM zones z JOIN deliver_details dd ON z.id = dd.zone_id
JOIN actual_times at ON z.id = at.zone_id
JOIN employees e ON dd.employee_id = e.id
GROUP BY z.name, at.eta_hours, e.name
UNION
SELECT z.name, 0, at.eta_hours, e.name
FROM zones z JOIN actual_times at ON z.id = at.zone_id,
employees e
WHERE e.id NOT IN (SELECT employee_id FROM deliver_details WHERE zone_id = z.id)
If you have only one employee for each zone the following query should work for you:
SELECT Z.name AS zone_name
,DT.hours_total
,ACT.eta_hours_total
,E.name AS employee_name
FROM zones Z
INNER JOIN (SELECT zone_id
, SUM(eta_hours) eta_hours_total
FROM actual_times
GROUP BY zone_id) ACT ON Z.zone_id = ACT.zone_id
INNER JOIN (SELECT zone_id
, employee_id
, SUM(hours) hours_total
FROM deliver_details
GROUP BY zone_id, employee_id) DT ON Z.zone_id = DT.zone_id
INNER JOIN employees E ON DT.employee_id = E.employee_id

count equal fields of 2 Tables and Inner join with 3rd

I have 3 tables
Table cases: docket is pk
| docket | dt_file |
-----------------------
|AA-0322 | 01-22-2015 |
|AA-0431 | 03-21-2014 |
Table parties:
| id | docket | name |
----------------------------
| 1 | AA-0322 | Bob |
| 2 | AA-0322 | John |
Table motions:
| id | docket | motion |
-----------------------------
| 1 | AA-0322 | Summons|
| 2 | AA-0322 | Guilty |
I want to count the number of fields in parties and motion that have the same docket and then make a table with table.cases with the dt_file
example:
| docket | party_count | motion_count| dt_file |
-----------------------------------------------
AA-0322| 2 | 2 | 02-22-2015|
I also want to filter by dt_file, so adding a WHERE statement ex:
WHERE YEAR(dt_file) = '2015'
So far i've came up with this, but I havn't had success joining table.cases with dt_file correctly.
SELECT p.docket, p.party_count, m.motion_count
FROM
(SELECT docket, COUNT(docket) AS party_count
FROM parties
GROUP BY docket) AS p
INNER JOIN
(SELECT docket, COUNT(docket) AS motion_count
FROM motions
GROUP BY docket) AS m
ON p.docket = m.docket
Your query seems correct. You just need to add cases to the from clause:
SELECT c.docket, p.party_count, m.motion_count, c.dt_file
FROM cases c JOIN
(SELECT docket, COUNT(docket) AS party_count
FROM parties
GROUP BY docket
) p
ON c.docket = p.docket INNER JOIN
(SELECT docket, COUNT(docket) AS motion_count
FROM motions
GROUP BY docket
) m
ON c.docket = m.docket;
If you want all dockets, even those where there are no motions or parties, then use LEFT JOIN.
Let this be another example.
Select
p.docket,
COUNT(p.docket) As party_count,
COUNT(m.docket) As motion_count,
p.dt_file
From
cases As c
Inner Join parties As p
On c.docket = p.docket
Inner Join motions As m
On c.docket = m.docket
Group By
p.docket,
p.dt_file
Try this:
SELECT p.docket, p.party_count, m.motion_count, c.dt_file
FROM
(SELECT docket, COUNT(docket) AS party_count
FROM parties
GROUP BY docket) AS p
INNER JOIN
(SELECT docket, COUNT(docket) AS motion_count
FROM motions
GROUP BY docket) AS m
ON p.docket = m.docket
INNER JOIN cases c
ON p.docket = c.docket
WHERE YEAR(c.dt_file) = '2015'

MYSQL query to get multiple statuses from statuses table

I've two tables (MySQL) in my application related to tracking jobs. The first table "jobs" contains one row for each job recorded in the system. This is supported by a jobs progress table which keeps a track of the progress of a job with a new row added each time a job moves to a new state. It is also possible that a job can be re-opened again and pushed back to a previous state. Therefore the job_progress table could have multiple entries for the same job state but on different date/times.
I'd like to be able to get the current state of a job by getting the max date for a particular state via the jobs_progress table. I'd also like to be able to get for each job the date that it progressed through each state. However I'm struggling to do this via SQL and would appreciate some help. See the following SQLFiddle: http://sqlfiddle.com/#!2/8b27f/4
Here's what I currently have:
SELECT j.*
, jp.job_state
, jp.effective_date
, jp.user_id
FROM jobs j
LEFT
JOIN jobs_progress jp
ON jp.job_id = j.id;
+----+-----------+-------------+-----------+-----------------------+-----------+---------------+---------------+---------------------+---------+
| id | agency_id | entity_type | entity_id | job_title | job_state | system_status | job_state | effective_date | user_id |
+----+-----------+-------------+-----------+-----------------------+-----------+---------------+---------------+---------------------+---------+
| 1 | 123 | PROPERTY | 61 | set of keys to be cut | INVOICED | ACTIVE | NEW | 2014-07-08 12:27:54 | 102 |
| 1 | 123 | PROPERTY | 61 | set of keys to be cut | INVOICED | ACTIVE | APPROVED | 2014-07-08 12:28:02 | 102 |
| 1 | 123 | PROPERTY | 61 | set of keys to be cut | INVOICED | ACTIVE | ASSIGNED | 2014-07-08 12:29:02 | 102 |
| 1 | 123 | PROPERTY | 61 | set of keys to be cut | INVOICED | ACTIVE | WORK_COMPLETE | 2014-07-08 12:29:11 | 102 |
| 1 | 123 | PROPERTY | 61 | set of keys to be cut | INVOICED | ACTIVE | INVOICED | 2014-07-08 12:29:27 | 102 |
| 1 | 123 | PROPERTY | 61 | set of keys to be cut | INVOICED | ACTIVE | ASSIGNED | 2014-08-21 12:29:02 | 103 |
| 1 | 123 | PROPERTY | 61 | set of keys to be cut | INVOICED | ACTIVE | WORK_COMPLETE | 2014-08-30 12:29:11 | 103 |
| 1 | 123 | PROPERTY | 61 | set of keys to be cut | INVOICED | ACTIVE | INVOICED | 2014-09-01 12:29:27 | 103 |
+----+-----------+-------------+-----------+-----------------------+-----------+---------------+---------------+---------------------+---------+
Here's what I want:
+----+-----------+-----------------------+---------------------+--------------------------+--------------------------+--------------------------------+-------------------------+
| id | agency_id | job_title | raised_date (NEW) | approved_date (APPROVED) | assigned_date (ASSIGNED) | completed_date (WORK_COMPLETE) | invoiced_date (INVOICED)|
+----+-----------+-----------------------+---------------------+--------------------------+--------------------------+--------------------------------+-------------------------+
| 1 | 123 | set of keys to be cut | 2014-07-08 12:27:54 | 2014-07-08 12:28:02 | 2014-08-21 12:29:02 | 2014-08-30 12:29:11 | 2014-09-01 12:29:27 |
+----+-----------+-----------------------+---------------------+--------------------------+--------------------------+--------------------------------+-------------------------+
And here's what I tried:
This will show you the maximum date for each status, so should contain everything you want?
select
j.id,
j.agency_id,
j.entity_type,
j.entity_id,
j.job_title,
j.system_status,
jp.jobstate2 job_state,
jp.effectivedate
from jobs j
inner join (select job_id,job_state jobstate2,max(effective_date) effectivedate
from jobs_progress
group by job_id,job_state) jp
on jp.job_id = j.id
order by effectivedate desc
EDIT: Following some more requirements being added
It looks liek you're after a PIVOTed output. As far as I know, there isn;t an easy way to do this in MySQL, but you could try this, which isn't pretty, but does produce the result you're after:
select
j.id,
j.agency_id,
j.entity_type,
j.entity_id,
j.job_title,
j.system_status,
j.job_state,
jp_new.effectivedate raised_date,
jp_approved.effectivedate approved_date,
jp_assigned.effectivedate assigned_date,
jp_complete.effectivedate complete_date,
jp_invoiced.effectivedate invoice_date
from jobs j
inner join (select job_id,max(effective_date) effectivedate
from jobs_progress
where job_state = 'NEW'
group by job_id,job_state) jp_new
on jp_new.job_id = j.id
inner join (select job_id,max(effective_date) effectivedate
from jobs_progress
where job_state = 'APPROVED'
group by job_id,job_state) jp_approved
on jp_approved.job_id = j.id
inner join (select job_id,max(effective_date) effectivedate
from jobs_progress
where job_state = 'ASSIGNED'
group by job_id,job_state) jp_assigned
on jp_assigned.job_id = j.id
inner join (select job_id,max(effective_date) effectivedate
from jobs_progress
where job_state = 'WORK_COMPLETE'
group by job_id,job_state) jp_complete
on jp_complete.job_id = j.id
inner join (select job_id,max(effective_date) effectivedate
from jobs_progress
where job_state = 'INVOICED'
group by job_id,job_state) jp_invoiced
on jp_invoiced.job_id = j.id
EDIT: Following some more requirements being added
If you want to show jobs that haven't been through all of the stages, then use LEFT OUTER JOIN instead of INNER JOIN:
select
j.id,
j.agency_id,
j.entity_type,
j.entity_id,
j.job_title,
j.system_status,
j.job_state,
jp_new.effectivedate raised_date,
jp_approved.effectivedate approved_date,
jp_assigned.effectivedate assigned_date,
jp_complete.effectivedate complete_date,
jp_invoiced.effectivedate invoice_date
from jobs j
left outer join (select job_id,max(effective_date) effectivedate
from jobs_progress
where job_state = 'NEW'
group by job_id,job_state) jp_new
on jp_new.job_id = j.id
left outer join (select job_id,max(effective_date) effectivedate
from jobs_progress
where job_state = 'APPROVED'
group by job_id,job_state) jp_approved
on jp_approved.job_id = j.id
left outer join (select job_id,max(effective_date) effectivedate
from jobs_progress
where job_state = 'ASSIGNED'
group by job_id,job_state) jp_assigned
on jp_assigned.job_id = j.id
left outer join (select job_id,max(effective_date) effectivedate
from jobs_progress
where job_state = 'WORK_COMPLETE'
group by job_id,job_state) jp_complete
on jp_complete.job_id = j.id
left outer join (select job_id,max(effective_date) effectivedate
from jobs_progress
where job_state = 'INVOICED'
group by job_id,job_state) jp_invoiced
on jp_invoiced.job_id = j.id
This will get you the last date a row was invoiced. If you want all the transition dates, how do you want that displayed?
select jobs.*, LastEntry.effective_date
from jobs
INNER JOIN (SELECT job_id, MAX(effective_date) AS effective_date FROM jobs_progress WHERE job_state = 'INVOICED' GROUP BY job_id) AS LastEntry ON jobs.id = LastEntry.job_id
This will only show jobs that have been invoiced. If you want to include jobs that have not been invoiced, then use a LEFT OUTER JOIN on the derived table instead of an inner join
You can get job with latest effective_date without sub query. Joins will perform fast than sub queries when you are working with more data.
--> latest job
SELECT jb.id,
jb_pr1.job_state,
jb_pr1.effective_date
FROM
jobs jb
INNER JOIN jobs_progress jb_pr1 on jb_pr1.job_id = jb.id
LEFT JOIN jobs_progress jb_pr2
ON (jb_pr1.job_id = jb_pr2.job_id AND jb_pr1.effective_date < jb_pr2.effective_date)
WHERE jb_pr2.effective_date IS NULL;
SQL Fiddle Link : http://sqlfiddle.com/#!2/8b27f/41

Trouble with join statement for selecting data across tables with MYSQL

I'm trying to display events that a user has created and events that he has signed up for. I have three tables for this.
//events
| event_id | event_title | event_details | event_timestamp | userid
1 title1 test 1234 1
2 title2 testing2 123 2
//registration_items : event_id references events.event_id
| id | event_id | task_name
1 2 task 1
//registration_signup : id references registration_items.id
| id | userid | timestamp
1 1 1234
Here's the current query I have. Right now it only displays the event the user created. It should display both created events and ones he signed up for
select events.*, registration_items.*, registration_signup.*, users.username from events
INNER JOIN users on users.userid = events.userid
LEFT JOIN registration_items ON registration_items.event_id = events.event_id
LEFT JOIN registration_signup ON registration_signup.id = registration_items.id
WHERE events.userid = '$user_id' OR registration_signup.userid = '$user_id' ORDER BY events.event_timestamp DESC
For userid1 the output should be
Title
title1 (the user created this)
title2 (the user signed up for this)
For userid2 the output should be
Title
title2
select events.*, registration_items.*, registration_signup.*, users.username
from events
INNER JOIN users on users.userid = events.userid
LEFT JOIN registration_items ON registration_items.event_id = events.event_id
LEFT JOIN registration_signup ON registration_signup.id = registration_items.id
WHERE registration_signup.userid = '$user_id'
union
select events.*, registration_items.*, registration_signup.*, users.username
from events
INNER JOIN users on users.userid = events.userid
INNER JOIN registration_items ON registration_items.event_id = events.event_id
INNER JOIN registration_signup ON registration_signup.id = registration_items.id
WHERE events.userid = '$user_id'
ORDER BY events.event_timestamp DESC
I am not sure if it's correct to guess that you have a typo in the sample data. Because your query works the moment you change the user id to 1 for both events, signed up events.. So please take a look at this reference demo and comment if it's not a typo...
SQLFIDDLE DEMO
query: (your query..)
select u.userid,e.event_id as id,
ri.event_id as evt, e.event_title,
ri.task_name,
rs.timestamp,
u.name from events e
INNER JOIN users u on
u.userid = e.userid
LEFT JOIN registration_items ri ON
ri.event_id = e.event_id
LEFT JOIN registration_signup rs ON
rs.id = ri.id
WHERE e.userid = '1' or
rs.userid = '1'
ORDER BY e.event_timestamp DESC
;
Results:
| USERID | ID | EVT | EVENT_TITLE | TASK_NAME | TIMESTAMP | NAME |
------------------------------------------------------------------
| 1 | 1 | 1 | title1 | task 1 | 1234 | john |
| 1 | 2 | 2 | title2 | task 1.2 | 3456 | john |

getting latest data in one to many relation table

i would like to fetch data from table a,b,c but order by most recent data of table response
table casework has this structure ( simplified):
casework_id | problem | user_id
------------+-----------+-------
1 | Problem1 | 1
2 | Problem2 | 2
3 | Problem3 | 1
4 | Problem4 | 3
table user has this structure ( simplified):
user_id | name
--------+-----------------
1 | peter
2 | Sam
3 | Tom
4 | Steve
table response has this structure ( simplified):
response_id | response | casework_id | created
------------+-----------+--------------+-------
1 | responce1 | 1 | 2012-10-14 11:28:31
2 | responce2 | 1 | 2012-9-10 11:28:31
3 | responce3 | 1 | 2012-9-2 11:28:31
4 | responce4 | 3 | 2012-8-3 11:28:31
4 | responce5 | 3 | 2012-8-2 11:28:31
I am looking the query to fetch data order by latest responce and group by casework_id
I. e. required out put is
casework_id | problem | name | responce | created
------------+-----------+-------+-----------+---------
1 | Problem1 | peter | responce1 | 2012-10-14 11:28:31
2 | Problem2 | Sam | Null | Null
3 | Problem3 | peter | responce4 | 2012-8-3 11:28:31
4 | Problem4 | Tom | Null | Null
I would be most grateful if one of you kind people could point me in the right direction.
You can use the following:
select c.casework_id,
c.problem,
u.name,
r2.response,
r1.created
from casework c
left join user u
on c.user_id = u.user_id
left join
(
select max(created) created, casework_id
from response r
group by casework_id
) r1
on c.casework_id = r1.casework_id
left join response r2
on r1.created = r2.created
and r1.casework_id = r2.casework_id
See SQL Fiddle with Demo
If you want to include both the user that created the casework and then who responsed, then you will want to join on the user table twice:
select c.casework_id,
c.problem,
u1.name CreatedByName,
r2.response,
r1.created,
u2.name ReponseName
from casework c
left join user u1
on c.user_id = u1.user_id
left join
(
select max(created) created, casework_id
from response r
group by casework_id
) r1
on c.casework_id = r1.casework_id
left join response r2
on r1.created = r2.created
and r1.casework_id = r2.casework_id
left join user u2
on r2.user_id = u2.user_id
See SQL Fiddle with demo
I have not tested it, but it might give you an idea
select c.casework_id, c.problem,
(select name from user u where u.user_id = c.user_id ),
(select r.reponse from response r where r.casework_id = c.casework_id ORDER BY r.created DESC LIMIT 1),
(select r.created from response r where r.casework_id = c.casework_id ORDER BY r.created DESC LIMIT 1),
from casework c
SELECT responce.casework_id, problem, name, responce, created
FROM responce
JOIN
(SELECT casework_id, problem, name
FROM casework JOIN user
ON casework.userid=user.userid) AS A
ON responce.casework_id=A.casework_id
ORDER BY responce, responce.casework_id
Try this
select c.caseword_id, c.problem, u.name, response.response, responce.created from asework c inner join user u on u.user_id = c.user_id left outer join select casework_id from response having max(created) group by casework_id) responsedata on responsedata.casework_id = c.casework_id