How to join three tables? Count, and Average SQL - mysql

I have three tables below with the following information
project.analytics
| proj_id | list_date | state
| 1 | 03/05/10 | CA
| 2 | 04/05/10 | WA
| 3 | 03/05/10 | WA
| 4 | 04/05/10 | CA
| 5 | 03/05/10 | WA
| 6 | 04/05/10 | CA
employees.analytics
| employee_id | proj_id | worked_date
| 20 | 1 | 3/12/10
| 30 | 1 | 3/11/10
| 40 | 2 | 4/15/10
| 50 | 3 | 3/16/10
| 60 | 3 | 3/17/10
| 70 | 4 | 4/18/10
email.forward
| email_id | event_id | employee_id
| 1 | 1 | 20
| 2 | 2 | 80
| 3 | 3 | 40
| 4 | 4 | 50
| 5 | 5 | 50
| 6 | 6 | 60
How can I determine which emails (email_id) drove employees to work on projects(amount) by month and state?
Desired output:
Email_id | Month | state |# of employee
1 | March | CA | 1
1 | April | WA | 2
2 | July | WA | 2
2 | August | CA | 1
I'm pretty stuck as I'm not sure how to maneuver the three tables together.
Essentially I want to be able to answer
This email_id had this many employees work on this project on this month and state.

Something like this should work:
SELECT c.email_id, month(b.worked_date), a.state, count(distinct b.employee_id)
FROM project.analytics a
JOIN employees.analytics b
ON a.project_id = b.project_id
JOIN email.forward c
on c.employee_id = b.employee_id
GROUP BY c.email_id, month(b.worked_date)
I am not sure what DBMS you use, so MONTH() may or may not exist there, but you should be able to find a similar function.

Related

How to join two tables such that same column from both the tables are not repeated? [duplicate]

This question already has answers here:
MySQL Removing duplicate columns on Left Join, 3 tables
(4 answers)
Closed 2 years ago.
I have following two tables
Table 1: employee
+----+---------+--------+--------+------+-------+------+----------+
| No | Name | Salary | Zone | Age | Grade | Dept | HireDate |
+----+---------+--------+--------+------+-------+------+----------+
| 1 | Mukul | 30000 | West | 28 | A | 10 | NULL |
| 2 | Kritika | 35000 | Centre | 30 | A | 10 | NULL |
| 3 | Naveen | 35200 | West | 40 | B | 20 | NULL |
| 4 | Uday | 41800 | North | 38 | C | 30 | NULL |
| 5 | Nupur | 32000 | East | 26 | B | 20 | NULL |
| 6 | Moksh | 37000 | South | 28 | B | 10 | NULL |
| 7 | Shelly | 36000 | North | 26 | A | 30 | NULL |
+----+---------+--------+--------+------+-------+------+----------+
Table 2:department
+------+---------+--------+--------+------+
| Dept | Dname | Minsal | Maxsal | HoD |
+------+---------+--------+--------+------+
| 10 | Sales | 25000 | 32000 | 1 |
| 20 | Finance | 30000 | 50000 | 5 |
| 30 | Admin | 25000 | 40000 | 7 |
+------+---------+--------+--------+------+
Suppose I want to display details of all the employees who work in the Sales department.
I tried INNER JOIN
SELECT *
FROM employee AS A
INNER JOIN department AS B
ON A.Dept = B.Dept AND B.Dname = "Sales";
This displayed this table
+----+---------+--------+--------+------+-------+------+----------+------+-------+--------+--------+------+
| No | Name | Salary | Zone | Age | Grade | Dept | HireDate | Dept | Dname | Minsal | Maxsal | HoD |
+----+---------+--------+--------+------+-------+------+----------+------+-------+--------+--------+------+
| 1 | Mukul | 30000 | West | 28 | A | 10 | NULL | 10 | Sales | 25000 | 32000 | 1 |
| 2 | Kritika | 35000 | Centre | 30 | A | 10 | NULL | 10 | Sales | 25000 | 32000 | 1 |
| 6 | Moksh | 37000 | South | 28 | B | 10 | NULL | 10 | Sales | 25000 | 32000 | 1 |
+----+---------+--------+--------+------+-------+------+----------+------+-------+--------+--------+------+
As you can see the Dept column is displayed twice.
So here are my questions
1. How to display same name columns only once?
2. Instead of INNER JOIN how else this can be done i.e Any better ways to do this?
Instead of selecting everything with * you need to specify the columns you want. You can also limit the * to all columns of a single table. Example:
SELECT e.*, d.Dname, d.HoD
FROM employee AS e
INNER JOIN department AS d ON e.Dept = d.Dept
WHERE d.Dname = "Sales";
Like this:
select A.No, A.Name, A.Salary, A.Zone, A.Age, A.Grade, A.Dept, A.HireDate,
B.Dname, B.Minsal, B.Maxsal, B.HoD
(...)
Inner join is the right way to join. No problems on this side.

Listing names with count of an argument in another table - mysql

Not sure how to describe it, ive been searching the internet for a few hours and have gone pretty numb. I have two tables. One called hotel, the other called room. Pretty self explanatory but hotel is a list of hotels with 'hotelNo' as a primary key and with their city and name and that, while room has other info and 'hotelNo' as a foreign key to the hotel primary key.
hotel
+---------+---------------------+----------+
| hotelNo | hotelName | city |
+---------+---------------------+----------+
| 1 | Grosvenor Hotel | Sydney |
| 2 | ANA Hotel | Sydney |
| 3 | Great Mansion Hotel | Brisbane |
| 4 | Grand Hotel | Brisbane |
| 5 | Central Hotel | Sydney |
| 6 | Airport Hotel | Sydney |
+---------+---------------------+----------+
and room
+--------+---------+------+--------+
| roomNo | hotelNo | type | rate |
+--------+---------+------+--------+
| 101 | 1 | S | 120.00 |
| 101 | 2 | D | 100.00 |
| 101 | 3 | S | 80.00 |
| 101 | 5 | D | 180.00 |
| 102 | 1 | S | 120.00 |
| 102 | 2 | S | 80.00 |
| 102 | 3 | S | 80.00 |
| 102 | 5 | K | 190.00 |
| 103 | 1 | S | 120.00 |
| 103 | 3 | D | 100.00 |
| 103 | 5 | S | 120.00 |
| 104 | 1 | D | 150.00 |
| 104 | 4 | Q | 100.00 |
| 104 | 5 | D | 140.00 |
| 105 | 1 | D | 150.00 |
| 105 | 4 | Q | 500.00 |
| 106 | 1 | D | 150.00 |
| 107 | 1 | Q | 180.00 |
| 108 | 1 | Q | 180.00 |
| 109 | 1 | Q | 180.00 |
+--------+---------+------+--------+
The question I am asked is
"List the names of hotels in Sydney that have 3 or more rooms."
and i want my output to just be the name of the hotels
+---------------------+
| hotelName |
+---------------------+
| Grosvenor Hotel |
| Central Hotel |
+---------------------+
Any help would be great, as i have some catching up todo. cheers
Just do what the above comment suggest, and join the two tables, aggregating by hotel:
SELECT
h.hotelNo,
h.hotelName
FROM hotel h
INNER JOIN room r
ON h.hotelNo = r.hotelNo
GROUP BY
h.hotelNo,
h.hotelName
HAVING
COUNT(*) >= 3;
Note that I select (and aggregate) using both the hotel name and the hotel's id number. This is to cover a situation where possibly two hotels in Australia would happen to have the same name.
As some spoilsports have shown the solution (sorry about that; this would have been a good execrcise for you), I am showing alternatives for the learning effect.
Use IN
You only want to select hotel names. So why join at all?
select hotelname
from hotels
where hotelno in
(
select hotelno
from rooms
group by hotelno
having count(*) >= 3
)
order by hotelname;
Aggregate before joining
If you want to show the count in the results, then join. Although the query gets longer and a tad more complicated, it is good style to aggregate before joining. This is especially helpful at times you need aggregates from different tables.
select h.hotelname, r.room_count
from hotels h
join
(
select hotelno, count(*) as room_count
from rooms
group by hotelno
having count(*) >= 3
) r on r.hotelno = h.hotelno
order by h.hotelname;

Querying a many to many relationship in SQL

I have the following SQL relationship
user has many games
games has may users
User
----
id | name | age |
__________________
1 | mike | 11 |
2 | jeff | 12 |
3 | jake | 31 |
4 | lemd | 81 |
Game
-----
id | name | time |
_____________________
1 | froyo | 11:10 |
2 | honey | 12:22 |
3 | combb | 13:00 |
4 | lolli | 14:00 |
User_Game
----------
| userid | game_id |
___________________
| 1 | 2 |
| 2 | 2 |
| 3 | 1 |
| 4 | 3 |
| 1 | 2 |
| 2 | 4 |
| 2 | 1 |
For each of the users is there a way to get a
list of games that they have played including the number
of games that each user participated in.
Edit
I tried this query
Select User.name, User.age
from User
inner join User_Game
on User.id=User_Game.userid;
However not sure how I could add the count to it
SELECT
userid,
GROUP_CONCAT(game_id) as game_list,
COUNT(*) as total_games
FROM
USER_GAME
GROUP BY
userid;

MySQL how to find averages / day for different clients with different creation days

I've tried the following queries but unfortunately they don't work :(.
Worth mentioning that each customer has more than one CustomerUsers
select (a.TotalJobs / b.DaysActive) from
(select count(jr.id) as TotalJobs
from jobrequests jr, customers c, customerusers cu
where jr.customeruserid=cu.id
and cu.customerid=c.id
group by c.name) as a,
(select datediff(curdate(), from_unixtime(c.CreationTime)) as DaysActive
from customers c
group by c.name) as b
Please see below the tables
Jobs:
+----+--------------+
| ID | JobRequestID |
+----+--------------+
| 1 | 1 |
| 2 | 1 |
| 3 | 1 |
| 4 | 1 |
| 5 | 1 |
| 6 | 2 |
| 7 | 2 |
| 8 | 3 |
| 9 | 3 |
| 10 | 3 |
| 11 | 4 |
| 12 | 4 |
| 13 | 5 |
| 14 | 5 |
| 15 | 6 |
| 16 | 7 |
| 17 | 8 |
| 18 | 8 |
| 19 | 9 |
| 20 | 10 |
+----+--------------+
JobRequests:
+----+---------------+
| ID | CustomeUserID |
+----+---------------+
| 1 | 1 |
| 2 | 1 |
| 3 | 2 |
| 4 | 2 |
| 5 | 2 |
| 6 | 3 |
| 7 | 4 |
| 8 | 4 |
| 9 | 4 |
| 10 | 5 |
| 11 | 5 |
| 12 | 5 |
| 13 | 6 |
| 14 | 6 |
| 15 | 7 |
+----+---------------+
CustomerUsers:
+----+------------+
| ID | CustomerID |
+----+------------+
| 1 | 1 |
| 2 | 1 |
| 3 | 1 |
| 4 | 2 |
| 5 | 2 |
| 6 | 2 |
| 7 | 2 |
| 8 | 3 |
| 9 | 3 |
| 10 | 4 |
+----+------------+
Customers:
+----+------+--------------+
| ID | Name | CreationTime |
+----+------+--------------+
| 1 | a | 1415814194 |
| 2 | b | 1415814194 |
| 3 | c | 1415986994 |
| 4 | d | 1415986994 |
+----+------+--------------+
For the moment it returns 16 results (4X4), dividing each result from 1st sub-query to each result from the 2nd one (each of these sub-queries return 4 results). Can anyone please help me to get this to divide only 1 result from sub-query 1 to it's corespondent from sub-query 2?
Thank you in advance.
I suspect that you can do what you want this a query like this:
select c.name, count(*) / (datediff(curdate(), from_unixtime(c.CreationTime))
from customerusers cu join
jobrequests jr
on jr.customeruserid = cu.id join
customers c
on cu.customerid = c.id
group by c.name;
I don't see why you need two subqueries for this.
I'm guessing you need to join your results together -- as currently written, you're producing a cartesian product.
Try something like this adding c.id to each subquery (it's better to group by it presumably rather than the name):
select (a.TotalJobs / b.DaysActive)
from (
select c.id,
count(jr.id) as TotalJobs
from jobrequests jr
join customers c on jr.customeruserid=cu.id
join customerusers cu on cu.customerid=c.id
group by c.id) a join (
select c.id,
datediff(curdate(), from_unixtime(c.CreationTime)) as DaysActive
from customers c
group by c.id) b on a.id = b.id
Please note, I've updated your syntax to use the more standard join syntax.

Creating a formulaic query with multiple columns and joined columns

I'm trying to produce a formula which pits our students' reward points against their negative behaviour flags.
Students are given LEAP points (in the transactions table) for their positive behaviour. They get more points depending on the category of their reward, i.e. Model Citizen gives the student 10 points.
On the other hand, students are given single Flags for negative behaviour. The category of the Flag is then weighted in a database table, i.e. the Aggressive Defiance category will have a high weighting of 4 whereas Low Level Disruption will only be worth 1.
The difficulty therefore is trying to factor in the Flag categories' weightings. They're stored in the categories table under the Weight column.
Here's the SQL fiddle: http://sqlfiddle.com/#!2/2e5756
In my head, the pseudo-SQL code would look something like this...
SELECT
CONCAT( stu.Surname, ", ", stu.Firstname ) AS `Student`,
SUM(t.Points) AS `LEAP Points`,
SUM(<<formula>>) AS `Flags`
( `LEAP Points` - `Flags` ) AS `Worked Out Points Thing`
FROM student stu
LEFT JOIN transactions t ON t.Recipient_ID = stu.id
LEFT JOIN flags f ON f.Student_ID = stu.id
LEFT JOIN categories c ON f.Category_ID = c.ID
GROUP BY stu.id
However, it's the <<formula>> that I have no idea how to implement in MySQL. It needs to be something like this:
SUM OF[ Each of Student's Flags * that Flag's Category Weighting ]
So, if a student has these flags...
#1 f.Reason "Being naughty", f.Category_ID "1", c.Title "Low Level Disruption", c.Weight "1"
#1 Reason "Aggressively naughty!", Category "Aggressive Defiance", Category Weighting "4"
#1 Reason "Missed detention", Category "Missed Detention", Category Weighting "3"
They would have a total of 1+4+3 = 9 points to use in the Worked Out Points Thing equation.
The desired output therefore is essentially...
Student LEAP Points Flags Equation Points LEAP Points minus Flag Points
D Wraight 1000 800 200
D Wraight2 500 800 -300
D Wraight3 1200 300 900
From the SQL fiddle above, here is the required output.. I've missed out some students because I had to work these out manually:
STUDENT FLAGS LEAP EQUATION
137608 4 (2+2) 12 (2+5+5) 8 (12-4)
139027 2 (2) 7 (2+5) 5 (7-2)
139041 4 (2+1+1+NULL) 8 (2+2+2+2) 4 (8-4)
139892 4 (4) 0 -4 (0-4)
138832 4 (4) 0 -4 (0-4)
34533 4 (4) 0 -4 (0-4)
137434 0 10 (2*5) 10 (10-0)
Which will help us to work out the choices we make available to each student when looking at end of year reward trips.
Hope that makes sense.. it's kinda boggled my head trying to explain it..
Thanks in advance,
figure out your 'formula' bit first because it's the deepest part. work outwards.
build a table of flags * weight per student
select sum(weight), student_id from flags f
join categories c
on f.category_id = c.id
group by student_id
so now you've got a table of flag values to minus from sum of transactions per student
select sum(points), recipient_id from transactions
group by recipient_id
so now we have two tables with positive and negative values by student id (assuming obviously that student id is recipient id)
you want those with transactions but without flags to appear in the result, so outer join.
and number minus null is null so ifnull function on the flags to get 0
select a.student, points - ifnull(penalties, 0) as netPoints
from
(select sum(points) as points, recipient_id as student from transactions
group by student) as a
left outer join
(select sum(weight) as penalties, student_id as student from flags f
join categories c
on f.category_id = c.id
group by student) as b
on
a.student = b.student
so with the name in there it's just
select
concat(firstname, ', ', surname) as name,
ifnull(points,0) as totalPoints,
ifnull(penalties,0) as totalPenalties,
ifnull(points,0) - ifnull(penalties, 0) as netPoints,
ifnull(countFlags, 0)
from
student
left join
(select sum(points) as points, recipient_id as student from transactions
group by student) as a
on student.id = a.student
left join
(select sum(weight) as penalties, count(f.id) as countFlags, student_id as student from flags f
join categories c
on f.category_id = c.id
group by student) as b
on
student.id = b.student
join condition is always from student's id column, which is never null.
there are probably more efficient ways, but who cares?
Returning to the question (and at the risk of repeating myself!), given the following data set, what would the desired result set look like...
SELECT * FROM flags;
+------+------------+----------+---------------------+-----------+-------------+--------------------------+---------------------+
| ID | Student_ID | Staff_ID | Datetime | Period_ID | Category_ID | Action_Taken_Category_ID | Action_Taken_Status |
+------+------------+----------+---------------------+-----------+-------------+--------------------------+---------------------+
| 8843 | 137608 | 35003 | 2014-03-11 08:31:00 | 8 | 16 | 7 | P |
| 8844 | 137608 | 35003 | 2014-03-11 08:31:00 | 8 | 16 | 7 | P |
| 8845 | 139027 | 35003 | 2014-03-11 08:31:00 | 8 | 16 | 7 | P |
| 8846 | 139041 | 35003 | 2014-03-11 08:31:00 | 8 | 16 | 7 | P |
| 8847 | 139041 | 34961 | 2014-03-11 09:01:02 | 2 | 12 | 26 | P |
| 8848 | 139041 | 34996 | 2014-03-11 09:23:21 | 3 | 12 | 27 | C |
| 8849 | 139041 | 35022 | 2014-03-11 11:07:46 | 4 | 34 | 28 | P |
| 8850 | 139892 | 138439 | 2014-03-11 11:12:47 | 4 | 21 | 7 | C |
| 8851 | 138832 | 138439 | 2014-03-11 11:12:48 | 4 | 21 | 7 | C |
| 8852 | 34533 | 138439 | 2014-03-11 11:12:48 | 4 | 21 | 7 | C |
+------+------------+----------+---------------------+-----------+-------------+--------------------------+---------------------+
SELECT * FROM categories;
+----+------+--------------------------------------+--------+----------+
| ID | Type | Title | Weight | Added_By |
+----+------+--------------------------------------+--------+----------+
| 10 | F | Low level disruption | 1 | NULL |
| 11 | F | Swearing directly at another student | 2 | NULL |
| 12 | F | Late | 1 | NULL |
| 13 | F | Absconded | 3 | NULL |
| 14 | F | Refusal to follow instruction | 3 | NULL |
| 15 | F | Smoking | 2 | NULL |
| 16 | F | No homework | 2 | NULL |
| 17 | F | Disruptive outside classroom | 2 | NULL |
| 18 | F | Eating/drinking in lesson | 1 | NULL |
| 19 | F | Incorrect uniform/equipment | 1 | NULL |
| 20 | F | Phone out in lesson | 3 | NULL |
| 21 | F | Aggressive defiance | 4 | NULL |
| 22 | F | Missed detention | 3 | NULL |
| 23 | F | Inappropriate behaviour/comments | 3 | NULL |
| 32 | F | IT Misuse | NULL | NULL |
| 34 | F | Inappropriate attitude towards staff | NULL | NULL |
| 35 | F | Care & Guidance | NULL | NULL |
+----+------+--------------------------------------+--------+----------+
SELECT * FROM transactions;
+----------------+------------+----------+--------------+--------+-------------+
| Transaction_ID | Datetime | Giver_ID | Recipient_ID | Points | Category_ID |
+----------------+------------+----------+--------------+--------+-------------+
| 34 | 2011-09-07 | 35019 | 137608 | 2 | 1 |
| 35 | 2011-09-07 | 35019 | 139027 | 2 | 1 |
| 36 | 2011-09-07 | 35019 | 139041 | 2 | 1 |
| 37 | 2011-09-07 | 35019 | 139041 | 2 | 1 |
| 38 | 2011-09-07 | 35019 | 139041 | 2 | 1 |
| 39 | 2011-09-07 | 35019 | 139041 | 2 | 1 |
| 40 | 2011-09-07 | 35019 | 137434 | 2 | 1 |
| 41 | 2011-09-07 | 35019 | 137434 | 2 | 1 |
| 42 | 2011-09-07 | 35019 | 137434 | 2 | 1 |
| 43 | 2011-09-07 | 35019 | 137434 | 2 | 1 |
| 44 | 2011-09-07 | 35006 | 137434 | 2 | 1 |
| 45 | 2011-09-07 | 35006 | 90306 | 2 | 1 |
| 46 | 2011-09-07 | 35006 | 90306 | 2 | 1 |
| 47 | 2011-09-07 | 35006 | 90306 | 2 | 1 |
| 48 | 2011-09-07 | 35023 | 137608 | 5 | 2 |
| 49 | 2011-09-07 | 35023 | 139027 | 5 | 2 |
| 50 | 2011-09-07 | 35023 | 139564 | 5 | 2 |
| 51 | 2011-09-07 | 35023 | 139564 | 5 | 2 |
| 52 | 2011-09-07 | 35023 | 139564 | 5 | 2 |
| 53 | 2011-09-07 | 35023 | 137608 | 5 | 3 |
+----------------+------------+----------+--------------+--------+-------------+
SELECT id,UPN,Year_Group,Tutor_Group,SEN_Status,Flags FROM student;
+--------+---------------+------------+-------------+------------+--------+
| id | UPN | Year_Group | Tutor_Group | SEN_Status | Flags |
+--------+---------------+------------+-------------+------------+--------+
| 137608 | A929238400044 | 11 | 11VID | A | |
| 139027 | A929238401045 | 10 | 10KS | | |
| 139041 | A929238402017 | 10 | 10RJ | A | FSM |
| 139892 | A929238403018 | 9 | 9BW | | |
| 139938 | A929238403020 | 9 | 9RH | | |
| 137434 | A929238500027 | 11 | 11VID | | |
| 138832 | A929238502002 | 10 | 10RY | A | FSM,PA |
| 34533 | A929238599028 | 0 | | | PA |
| 139564 | A929241500025 | 12 | | | PA |
| 90306 | A929253100006 | 12 | SLH | A | PA |
+--------+---------------+------------+-------------+------------+--------+