Get values from 3 tables into a summarized table SQL - mysql

I've tried other solutions I've found here but I don't get the correct information back.
I have a table with many different records. A list of names with a status of active. Then I have another table which holds information for each name with a ticket number and then an assignment 'Assigned' and 'Feedback'. Not all names have a ticket.
Then 1 more table that holds a number of hours that goes towards that ticket number.
I want a summary of this information for each name whether there is info there or not. So I started with a subquery here is what I have.
select z.name as 'Name', round(coalesce(sum(x.Hours),0),2) as "Assigned",
round(coalesce(sum(y.Hours),0),2) as "Feedback" from
(select name from namelist where status = 'Active') as z
left join
(select e.realname as "Name", b.id as "Ticket", b.status as "Status", c.value -
COALESCE(sum(a.Hours),0) as "Hours" from user_table e
join ticket_table b ON b.handler_id = e.id
join custom_table c ON c.bug_id = b.id AND c.field_id = 7
left custom_table d ON d.bug_id = b.id AND d.field_id = 15
left hours_table a ON a.Ticket = b.id
where (b.status = 50)
Group By b.id
ORDER BY `Name` ASC, `Status` DESC) x on z.Name= x.Name
left JOIN
(select e.realname as "Name", b.id as "Ticket", b.status as "Status", c.value -
COALESCE(sum(a.Hours),0) as "Hours" from user_table e
join ticket_table b ON b.handler_id = e.id
join custom_table c ON c.bug_id = b.id AND c.field_id = 7
left custom_table d ON d.bug_id = b.id AND d.field_id = 15
left hours_table a ON a.Ticket = b.id
where (b.status = 20)
Group By b.id
ORDER BY `Name` ASC, `Status` DESC) y on z.Name= y.Name
Group by Name
I've changed some of the names around but this is the basic idea. b.status = 50 means Assigned, and 20 means Feedback. Those joins create a table that looks like this:
---------------------------------------------------------------------------------------------------
| Name | Ticket | Status| Hours ((value from custom_table)-(sum from hours table based on ticket))|
| Joe | 234 | 50 | 20 |
| Joe | 235 | 50 | 30 |
| Joe | 236 | 50 | 40 |
| John | 233 | 50 | 10 |
| John | 237 | 50 | 20 |
| John | 238 | 50 | 20 |
---------------------------------------------------------------------------------------------------
---------------------------------------------------------------------------------------------------
| Name | Ticket | Status| Hours ((value from custom_table)-(sum from hours table based on ticket))|
| Joe | 134 | 20 | 60 |
| Joe | 135 | 20 | 30 |
| Joe | 136 | 20 | 40 |
| John | 133 | 20 | 70 |
| John | 137 | 20 | 20 |
| John | 138 | 20 | 20 |
---------------------------------------------------------------------------------------------------
-----------------
| Name | Status |
| Joe | Active|
| John | Active|
| Mary | Active|
| Tom | Active|
| John |Inactive|
-----------------
Desired result:
----------------------------
| Name | Assigned| Feedback|
| Joe | 90 | 130 |
| John | 50 | 110 |
| Mary | 0 | 0 |
| Tom | 0 | 0 |
----------------------------
Now the Hours table is c.value which is a 1 to 1 relation subtract sum(hours) from hours table 1 to many relationship.
If I take out one of the joins, the table works. When I put them together like this, the numbers are incorrect. I can get the assigned correct if I only use that join. I can get the feedback numbers correct if I only use the feeback join. However it doesn't work when trying to get either from them. Let me know if you need more info I'll try my best to provide.
Example results:
----------------------------
| Name | Assigned| Feedback|
| Joe | 392 | 145 |
| John | 125 | 94 |
| Mary | 0 | 0 |
| Tom | 0 | 0 |
----------------------------
If I just use the table with status 50.
----------------------------
| Name | Assigned|
| Joe | 90 |
| John | 50 |
| Mary | 0 |
| Tom | 0 |
----------------------------
If I just use the table with status 20.
----------------------------
| Name | Assigned|
| Joe | 130 |
| John | 110 |
| Mary | 0 |
| Tom | 0 |
----------------------------
Don't worry about the custom tables so much, there is a reason they are there but aren't a part of my question. The biggest thing is simply getting the c.value from there, the other join to that table is only for another status, but not relevant to what I'm trying to accomplish.

The two Left Joins seem identical to me apart from the status (unless I am missing something)
Have you tried using a single left join and then an aggregation with a Case statement i.e.
SELECT Name,
SUM(CASE WHEN Status = 50 THEN Hours ELSE 0 END) AS Assigned,
SUM(CASE WHEN Status = 20 THEN Hours ELSE 0 END) AS Feedback
FROM table
GROUP BY Name
I appreciate the answer here is over-simplified but it's more a suggestions since I don't know the content of all the tables mentioned in the query
SQL FIDDLE Example (apologies using SQL SERVER but logic in MySQL is the same for this simple example)
http://sqlfiddle.com/#!18/f77e4/5/0
The output matches the desired results but the issue might lie with another table.
This assumes a single table but the logic should work the same using a left join

Related

MySQL Selecting Rows and Grouping

I have a ( Joomla) database table called field_values, the contents are below;
+----+----------+---------+---------+
| id | field_id | item_id | value |
+----+----------+---------+---------+
| 1 | 2 | 446 | Jones |
| 2 | 2 | 447 | Smith |
| 3 | 2 | 448 | Jenkins |
| 4 | 3 | 446 | Paul |
| 5 | 3 | 447 | Peter |
| 6 | 3 | 448 | Sally |
| 7 | 4 | 446 | London |
| 8 | 4 | 447 | Dublin |
| 9 | 4 | 448 | Paris |
+----+----------+---------+---------+
I'm only displaying 9 rows from the table, but I actually have thousands, so the successful query would need to take this into account.
Columns explained;
id (primary / auto-increment)
field_id (FK to another fields table, 2 = surname, 3 = first name, 4 = location)
item_id (FK to another users table)
value (contents of field)
How can I select all the values from the above table but display them as follows;
+------------+-----------+----------+
| first_name | last_name | location |
+------------+-----------+----------+
| Paul | Jones | London |
| Peter | Smith | Dublin |
| Sally | Jenkins | Paris |
+------------+-----------+----------+
The id field isn't really necessary in the desired results above, I just added it to emphasise that each row is unique.
I'm not sure if I need to use a subquery or group by, maybe neither?
Thanks in advance.
A pivot query should work here:
SELECT
MAX(CASE WHEN field_id = 3 THEN value END) AS first_name,
MAX(CASE WHEN field_id = 2 THEN value END) AS last_name,
MAX(CASE WHEN field_id = 4 THEN value END) AS location
FROM yourTable
GROUP BY
item_id
ORDER BY
item_id;
Your current table structure is a denormalized key value store, a style which WordPress uses in some of its tables.
you could avoid subquery and grou by.
You could use the same table 3 times
select a.id, b.value firts_name, a.value last_name , c.value location
from field_values a
inner join field_values b on a.item_id = b.item_id and b.field_id = 3
inner join field_values bc on a.item_id = c.item_id and b.field_id = 4
where a.item_id = 2

solve mysql query

Today I have been asked a question by an interviewer that stated
we have three tables named as table A, B, and C.
Those tables are like this
A B C
------------------ -------------------------- ----------------------------
| ID | ProjectID | | ID | LocationID | aID | | ID | points | LocationID |
------------------ -------------------------- ----------------------------
| 1 | 15 | | 1 | 131 | 1 | | 1 | 123333 | 131 |
| 2 | 15 | | 2 | 132 | 1 | | 2 | 123223 | 132 |
| 3 | 15 | | 3 | 133 | 1 | | 3 | 522 | 211 |
| 4 | 12 | | 4 | 134 | 2 | | 4 | 25 | 136 |
------------------ | 5 | 136 | 2 | | 5 | 25 | 133 |
| 6 | 137 | 3 | | 6 | 25 | 134 |
| 7 | 138 | 1 | | 7 | 25 | 135 |
-------------------------- ----------------------------
now he told me to write a query that sums the points of those locations whose project is 15.
First i wrote the query to get ID's from table A like this
SELECT ID from A where projectID = 15
then i pass this result in table b query just like this
SELECT LocationID FROM B WHERE aID IN ( SELECT ID from A where projectID = 15 )
Then i calculate the sum of these locations just like this
SELECT SUM(points) from C where LocationID IN(SELECT LocationID FROM B WHERE aID IN ( SELECT ID from A where projectID = 15))
My Result is fine and query is correct. But he rejected my answer by saying that this nested IN Clause will slow down the whole process as when we have thousands of records.
Then he gave me another chance to review my answer but i couldn't figure it out.
Is there anyway to optimize this or is there some other way to do the same.
Any help? Thanks
Try this it may solve your problem.
Select SUM(C.points) FROM C JOIN B ON C.LocationID = B.LocationID JOIN A ON B.aID = A.ID where A.ProjectID = 15 GROUPBY A.ProjectID
Try with this....i hope it will work
select sum(c.points) as sum_points
from A a,B b,C c where
a.ID=b.aID and
b.LocationID=c.LocationID
and a.projectID=15

Grouping by date and combining data from 3 tables

The following are the three tables I have where session.id = signup.session_id AND session.loc_id = location.id. The max override is as the name suggest override the default max capacity for the location hence IFNULL(session.max_override, location.max_cap).
mysql> SELECT * FROM session;
+----+---------------------+---------------+--------+
| id | date_time | max_override | loc_id |
+----+---------------------+---------------+--------+
| 1 | 2014-02-04 10:30:00 | 35 | 2 |
| 2 | 2014-02-04 17:00:00 | | 2 |
| 3 | 2014-02-06 11:30:00 | 50 | 2 |
| 4 | 2014-02-09 13:30:00 | | 1 |
+----+---------------------+---------------+--------+
mysql> SELECT * FROM location;
+-----------------+---------+
| id | location | max_cap |
+-----------------+---------+
| 1 | up | 20 |
| 2 | down | 103 |
| 3 | right | 50 |
| 4 | left | 50 |
+-----------------+---------+
mysql> SELECT * FROM signups;
+-----------------+------------+
| id | name | session_id |
+-----------------+------------+
| 1 | test | 3 |
| 2 | admin | 1 |
| 3 | meme | 2 |
| 4 | anna | 4 |
+-----------------+------------+
The report I am trying to create looks simple but I am not sure how to approach the problem. The following is how I would like the report/output to look like..
mysql> query ouput;
+------------+----------+-----------+----------+----------+-----------+----------+
| date | am_time | am_ses_id | am_spots | pm_time | pm_ses_id | pm_spots |
+------------+----------+-----------+----------+----------+-----------+----------+
| 2014-02-04 | 10:30 AM | 1 | 34 | 05:00 PM | 2 | 102 |
| 2014-02-06 | 11:30 AM | 3 | 49 | | | |
| 2014-02-09 | | | | 01:30 PM | 4 | 49 |
+------------+----------+-----------+----------+----------+-----------+----------+
I can group the date and time correctly and also managed to get the session_id to match since it is all within one table but to calculate the am/pm spots which is nothing but counting the records in signups table for a particular session and deducting the value from either the max_cap or max_override depending on the situation.
THIS is what I tried
Using the following query
SELECT
DATE_FORMAT(a.date_time,'%m/%d/%Y') AS ses_date,
DATE_FORMAT(a.date_time,'%r') AS ses_time,
a.id,
COUNT(b.id) as signed_up,
IFNULL(a.max_override,c.max_cap) AS cap
FROM
test.session a
LEFT JOIN
test.signups b
ON (b.session_id = a.id)
LEFT JOIN
test.location c
ON (c.id = a.loc_id)
GROUP BY b.session_id
I get the following output
+------------+----------+--------+-----------+------+
| date | ses_time | ses_id | signed_up | cap |
+------------+----------+--------+-----------+------+
| 2014-02-04 | 10:30 AM | 1 | 1 | 35 |
| 2014-02-04 | 05:00 PM | 2 | 1 | 103 |
| 2014-02-06 | 10:30 AM | 3 | 1 | 50 |
| 2014-02-09 | 10:30 AM | 4 | 1 | 50 |
+------------+----------+--------+-----------+------+
But I cannot seem to find a way to group it only by the date so the output would appear like desired! I don't know if I should union two queries either.
Here is a very convoluted way of doing it...
sqlfiddle: http://sqlfiddle.com/#!2/d85ca/11
select c.ses_date `date`, a.ses_time am_time, a.id am_ses_id, a.cap-a.signed_up am_spots,
b.ses_time pm_time, b.id pm_ses_id, b.cap-b.signed_up pm_spots
from (
select distinct DATE_FORMAT(a.date_time,'%m/%d/%Y') ses_date
from session a) c
left join (
SELECT
DATE_FORMAT(a.date_time,'%m/%d/%Y') AS ses_date,
DATE_FORMAT(a.date_time,'%r') AS ses_time,
a.id,
COUNT(b.id) as signed_up,
IFNULL(a.max_override,c.max_cap) AS cap
FROM
session a
LEFT JOIN
signups b
ON (b.session_id = a.id)
LEFT JOIN
location c
ON (c.id = a.loc_id)
where date_format(a.date_time, '%p') = 'AM'
GROUP BY b.session_id) a on c.ses_date = a.ses_date
left join (
SELECT
DATE_FORMAT(a.date_time,'%m/%d/%Y') AS ses_date,
DATE_FORMAT(a.date_time,'%r') AS ses_time,
a.id,
COUNT(b.id) as signed_up,
IFNULL(a.max_override,c.max_cap) AS cap
FROM
session a
LEFT JOIN
signups b
ON (b.session_id = a.id)
LEFT JOIN
location c
ON (c.id = a.loc_id)
where date_format(a.date_time, '%p') = 'PM'
GROUP BY b.session_id) b on c.ses_date = b.ses_date;
You need to use the JOIN operator to let the SQL DB know the relationship between the tables.
In this case also, it may be easier to do a subquery to get the count (to avoid GROUP BY). I've not separated out AM and PM by day but you could do that.
SELECT session.date_time,
IFNULL(session.max_override,location.max_cap)-(
SELECT COUNT(signups.id)
FROM signups
WHERE signups.session_id = session.id) as avail_spots
FROM session LEFT JOIN location ON session.loc_id = location.id;
Note the LEFT JOIN will include 2014-02-04 17:00:00 with a NULL avail_spots since neither max_override nor max_cap have a value, whereas INNER JOIN would not report that session at all.
fiddle
EDIT: once you have the information by day you can use it on output. Trying to pivot out the times but grouping on the date adds a lot of complexity to the query that could be solved a lot more simply by whatever program you are using for your UI.

group and get value in table

I have 2 tables
Users
----------------
user_id | name
----------------
33 | jon
35 | igor
40 | mark
event
-----------------------------------
event_id | user_id | lavel | value
-----------------------------------
1 | 33 | status| 1
2 | 33 | ins | alfa
3 | 33 | time | 14:30
4 | 35 | status| 0
5 | 35 | ins | beta
6 | 35 | time | 14:51
7 | 40 | ins | beta
intended Query Result
------------------------
user_id | name | status
------------------------
33 | jon | 1
35 | igor | 0
40 | mark |
I want the users in the table and their status.
The status is a administration field. Can not be.
Can anyone help with the query?
SELECT u.user_id, u.name,
CASE WHEN e.value IS NULL THEN '' ELSE e.value AS status
FROM users u
LEFT JOIN event e ON e.user_id = u.user_id
WHERE e.lavel = 'status'
If you can, it seems like it would be a much more manageable table if your column headers were like this:
-----------------------------------------
event_id | user_id | status | ins | time
-----------------------------------------
Try this syntax :
select
user.id,
user.name,
event.value
from user inner join event
on user.id=ever.user_id
WHERE event.lavel = 'status';

Joining 3 tables

First off, sorry if this is a near enough duplicate. I've found this question, which nearly does what I want, but I couldn't wrap my head around how to alter it to my needs.
I've got these 3 tables:
cs_Accounts:
+----+-----------------------------+-------------+
| id | email | username |
+----+-----------------------------+-------------+
| 63 | jamasawaffles#googlil.com | jamwaffles2 |
| 64 | jamwghghhfles#goomail.com | jamwaffles3 |
| 65 | dhenddfggdfgetal-pipdfg.com | dhendu9411 |
| 60 | jwapldfgddfgfffles.co.uk | jamwaffles |
+----+-----------------------------+-------------+
cs_Groups:
+----+-----------+------------+-------------+
| id | low_limit | high_limit | name |
+----+-----------+------------+-------------+
| 1 | 0 | 0 | admin |
| 2 | 1 | 50 | developer |
| 3 | 76 | 100 | reviewer |
| 4 | 51 | 75 | beta tester |
| 5 | 1 | 50 | contributor |
+----+-----------+------------+-------------+
cs_Permissions:
+----+---------+----------+
| id | user_id | group_id |
+----+---------+----------+
| 4 | 60 | 4 |
| 3 | 60 | 1 |
| 5 | 60 | 2 |
| 6 | 62 | 1 |
| 7 | 62 | 3 |
+----+---------+----------+
I've been wrestling with a 3 way join for hours now, and I can't get the results I want. I'm looking for this behaviour: a row will be returned for every user from cs_Accounts where there is a row in cs_Permissions that contains their ID and the ID of a group from cs_Groups, as well as the group with the group_id has a high_lmiit and low_limit in a range I can specify.
Using the data in the tables above, we might end up with something like this:
email username cs_Groups.name
----------------------------------------------------------
jwapldfgddfgfffles.co.uk jamwaffles admin
jwapldfgddfgfffles.co.uk jamwaffles developer
jwapldfgddfgfffles.co.uk jamwaffles beta tester
dhenddfggdfgetal-pipdfg.com dhendu9411 admin
dhenddfggdfgetal-pipdfg.com dhendu9411 reviewer
There is an extra condition, however. This condition is where rows are only selected if the group the user belongs to has a high_limit and low_limit with values I can specify using a WHERE clause. As you can see, the table above only contains users with rows in the permissions table.
This feels a lot like homework but with a name like James I'm always willing to help.
select a.email,a.username,g.name
from cs_Accounts a
inner join cs_Permissions p on p.user_id = a.id
inner join cs_Groups g on g.id = p.Group_id
where g.low_limit > 70
and g.high_limt < 120
This is the query
SELECT ac.email, ac.username, gr.name
FROM cs_Accounts AS ac
LEFT JOIN cs_Permissions AS per ON per.user_id = ac.id
INNER JOIN cs_Groups AS gr ON per.user_id = gr.id
You can add a WHERE clause to this query if you want