MySQL join with a subquery - mysql

I have three tables and am trying to get info from two and then perform a calculation on the third and display all the results in one query.
The (simplified) tables are:
table: employee_work
employee_id name
1 Joe
2 Bob
3 Jane
4 Michelle
table: carryover
employee_id days
1 5
2 10
3 3
table: timeoff
employee_id time_off_type days
1 Carryover 2
1 Leave 3
1 Carryover 1
2 Sick 4
2 Carryover 4
3 Leave 1
4 Sickness 4
The results I would like are:
employee_id, carryover.days, timeoff.days
1 5 3
2 10 4
3 3 0
However when I run the query, whilst I get the correct values in columns 1 and 2, I get the same number repeated in the third column for all entries.
Here is my query:
Select
employee_work.employee_id,
carryover.carryover,
(SELECT SUM(days) FROM timeoff WHERE timeoff.time_off_type = 'Carryover'
AND timeoff.start_date>='2013-01-01') AS taken
From
carryover Left Join
employee_work On employee_work.employee_id = carryover.employee_id Left Join
timeoff On employee_work.employee_id = timeoff.employee_id Left Join
Where
carryover.carryover > 0
Group By
employee_work.employee_id
I have tried to group by in the sub query but I then get told "Subquery returns more than one row" - how can I ensure that the sub query is respecting the join so it only looks at each employee at a time so I get my desired results?

The answer to your question is to use a correlated subquery. You don't need to mention the timeoff table twice in this case:
Select
employee_work.employee_id,
carryover.carryover,
(SELECT SUM(days)
FROM timeoff
WHERE timeoff.time_off_type = 'Carryover' and
timeoff.start_date>='2013-01-01' and
timeoff.employee_id = employee_work.employee_id
) AS taken
From
carryover Left Join
employee_work On employee_work.employee_id = carryover.employee_id
Where
carryover.carryover > 0
Group By
employee_work.employee_id;
An alternative structure is to do the grouping for all employees in the from clause. You can also remove the employee_work table, because it does not seem to be being used. (You can use carryover.employee_id for the id.)
Select co.employee_id, co.carryover, et.taken
From carryover c Left Join
(SELECT employee_id, SUM(days) as taken
FROM timeoff
WHERE timeoff.time_off_type = 'Carryover' and
timeoff.start_date>='2013-01-01'
) et
on co.employee_id = et.employee_id
Where c.carryover > 0;
I don't think the group by is necessary. If it is, then you should probably have an aggregation function in the original query.

Related

SUM, GROUP BY AND LEFT JOIN in query

I've two tables:
`orders`
order_id order_office_id order_invoice_id
1 1 1
2 2 2
3 2 2
4 2 3
5 1 4
`invoices`
inv_id inv_order_id inv_amount
1 1 500.00
2 0 320.00
3 3 740.00
4 4 160.00
With this query:
SELECT SUM(inv_amount) matrah, order_office_id
FROM `invoices`
LEFT JOIN orders ON order_invoice_id = inv_id OR inv_order_id = order_id
WHERE order_id IS NOT NULL
GROUP BY order_office_id
It is multiplying some amounts.
What I want to get sum of amounts by office:
office_id sum
1 660.00
2 1060.00
How to get proper amounts by single query?
This does what you want, I think
select sum(inv_amount) as matrah,
(select order_office_id from orders where order_invoice_id = inv_id limit 1) as office
from invoices
group by office;
I've removed the OR, because you were getting two office ids for one of the orders, so it was ambiguous. I've included a subquery to make sure that only one office is applied to each order.
This query may help you out:
SELECT SUM(i.inv_amount) matrah, o.order_office_id
FROM `invoices` AS i
LEFT JOIN `orders` AS o
ON o.order_invoice_id = i.inv_id
GROUP BY order_office_id
OR statement in your ON condition caused the problem.

Select sum of zero if no records in second table?

I did some research and learned about the COALESCE(sum(num), 0) function. The issue is the example I found only related to using one table.
I am calculating a sum from a second table, and if there are no records for an item in the second table, I still want it to show up in my query and have a sum of zero.
SELECT note.user, note.product, note.noteID, note.note, COALESCE(sum(noteTable.Score), 0) as points
FROM note, noteTable
WHERE note.user <> 3 AND note.noteID = noteTable.noteID
I am only recieving results if there is an entry in the second table noteTable. If there are scores added for a note, I still want them to show up in the result with a points value of zero.
Table Examples:
Note
user | product | noteID |note
3 1 1 Great
3 2 2 Awesome
4 1 3 Sweet
NoteTable
noteID | score
1 5
The query should show me this:
user | noteID | sum(points)
3 1 5
3 2 0
4 3 0
But I am only getting this:
user | noteID | sum(points)
3 1 5
http://sqlfiddle.com/#!9/aae812/2
SELECT
note.user,
note.product,
note.noteID, note.note,
COALESCE(sum(noteTable.Score),0) as points
FROM note
LEFT JOIN noteTable
ON note.noteID = noteTable.noteID
WHERE note.user <> 3
and I guess you should add:
GROUP BY note.noteid
if you expect to get SUM for every user. So you want to get more then 1 record back.
First, learn to use proper JOIN syntax and table aliases. The answer to your question is SUM() along with COALESCE():
SELECT n.user, n.product, n.noteID, n.note,
COALESCE(sum(nt.Score), 0) as points
FROM note n LEFT JOIN
noteTable nt
ON n.noteID = nt.noteID
WHERE n.user <> 3
GROUP BY n.user, n.product, n.noteID, n.note;
You also need a GROUP BY.

MySQL UPDATE with GROUP and ORDER

I'm trying to make an update on a table so that it can increment the values on 1 column depending on another's order.
Here's how it'd go
ID GROUP_ID ORDER(Desired) ORDER(NOW)
1 1 1 2
2 1 2 3
3 1 3 1
4 2 1 2
5 2 2 1
6 3 1 1
7 3 2 1
8 3 3 2
So what I need is for each ID, to update the ORDER column so it can be consecutive, starting from 1, within each GROUP_ID.
I have found some solutions to similar problems regarding the updates and orders, but none that uses multiple orders for groups within the same table.
Hope I illustrated the problem right. Thanks in advance
You can do it by "ranking" the rows over again. Mysql doesn't support window functions but you can achieve the same results with join and count like this:
UPDATE YourTable t
INNER JOIN(SELECT s.id,s.group_id,count(*) as cnt
FROM YourTable s
INNER JOIN YourTable ss
ON(s.group_id = ss.group_id and s.id >= ss.id)
GROUP BY s.id,s.group_id) tt
ON (t.id = tt.id and t.group_id = tt.group_id)
SET t.order = tt.cnt

MySQL Select rows that match multiple rows in related table

The following tables are much larger, but have been downsized for ease of the question
Table 1 - exercise_rolladex
Exercise_ID | Exercise_Name
---------------------------
1 Pushups
2 Turkish Get Ups
3 Squats
4 Ice Skater
Table 2 - exercise_planes
Exercise_Plane_ID | Exercise_Plane
----------------------------------
1 Sagittal
2 Frontal
3 Transverse
Table 3 - exercise_has_planes
Exercise_ID | Exercise_Plane_ID
-------------------------------
1 1
2 1
2 2
2 3
3 1
4 2
4 3
My question is: How can I structure a Query where I can find the Exercise_ID of each exercise which has Exercise_Plane_ID=1 AND Exercise_Plane_ID=2. In other words, find the exercises that have both Sagittal AND Frontal planes of motion.
The Correct Query
SELECT e.Exercise_Name, p.Exercise_Plane
FROM exercise_rolladex e
INNER JOIN exercise_has_planes h ON h.Exercise_ID=e.Exercise_ID
INNER JOIN exercise_planes p ON p.Exercise_Plane_ID=h.Exercise_Plane_ID
WHERE p.Exercise_Plane_ID IN(2,1)
GROUP BY e.Exercise_ID
HAVING COUNT(DISTINCT h.Exercise_Plane_ID ) >= 2
UPDATE FOLLOW UP QUESTION
How then would I include an exclusion? for example, find the exercises with plane_id 2 and 3, but exclude exercises with plane_id 1 (The correct result being "Ice Skater")
I went ahead and answered my own question:
SELECT e.Exercise_Name, p.Exercise_Plane
FROM exercise_rolladex e
INNER JOIN exercise_has_planes h ON h.Exercise_ID=e.Exercise_ID
INNER JOIN exercise_planes p ON p.Exercise_Plane_ID=h.Exercise_Plane_ID
WHERE p.Exercise_Plane_ID IN(2,3)
AND e.Exercise_ID NOT IN
(SELECT Exercise_ID FROM exercise_has_planes WHERE Exercise_Plane_ID='1')
GROUP BY e.Exercise_ID
HAVING COUNT(DISTINCT h.Exercise_Plane_ID ) >= 2
Thanks to Mr. Brownstones answer from a different question.
SQL query to exclude items on the basis of one value
You can do something like this,this will check the plan id with your given input ids and filter out there count in each exercise group if count returns more than one then it means exercise has planes,having clause will fulfill the scenario of having both planes in exercise
SELECT e.Exercise_Name,
p.Exercise_Plane
FROM exercise_rolladex e
INNER JOIN exercise_has_planes h ON h.Exercise_ID=e.Exercise_ID
INNER JOIN exercise_planes p ON p.Exercise_Plane_ID=h.Exercise_Plane_ID
WHERE p.Exercise_Plane_ID IN(2,1)
GROUP BY e.Exercise_ID
HAVING COUNT(DISTINCT h.Exercise_Plane_ID ) >= 2
Demo

MySQL query, COUNT and SUM with two joined tables

I need a little help with a MySQL query.
I have two tables one table is a list of backlinks with a is_homepage (bool) flag. The second table is a list of the domains for all of the backlinks, a was link_found (bool) flag, and a url_count column which is the number of rows in the backlinks table that are associated with each domain.
Note that the domain_id column is the foreign key to the domain table id column. Heres some sample data.
backlinks
id domain_id is_homepage page_href
1 1 1 http://ablog.wordpress.com/
2 1 0 http://ablog.wordpress.com/contact/
3 1 0 http://ablog.wordpress.com/archives/
4 2 1 http://www.somewhere.org/
5 2 0 http://www.somewhere.org/page=3
6 3 1 http://www.great-fun-site.com/
7 3 0 http://www.great-fun-site.com/index.html
8 4 0 http://red.blgspot.com/page=7
9 4 0 http://blue.blgspot.com/page=9
domains
id url_count link_found domain_name
1 3 1 wordpress.com
2 2 0 somewhere.org
3 2 1 great-fun-site.com
4 2 1 blgspot.com
The results Im looking to get from the above data would be: count = 2, total = 5.
Im trying to get the count of rows from the domains table (count) and then the sum of the url_count (total) from the domains table WHERE link_found is 1 and where one of the links in the backlink table is_homepage is 1.
Here's the query I'm trying to work with.
SELECT SUM(1) AS count, SUM(`url_count`) total
FROM `domains` AS domain
LEFT JOIN `backlinks` AS link ON link.domain_id = domain.id
WHERE domain.id IN (
SELECT DISTINCT(bl.domain_id)
FROM `backlinks` AS bl
WHERE bl.tablekey_id = 11
AND bl.is_homepage = 1
)
AND domain.link_found = 1
AND link.is_homepage = 1
GROUP BY `domain`.`id`
The problem with this query is that it returns a row for each entry in the domains table. I think I might need one more sub query to add up the returned results but I'm not sure if that's correct. Does anyone see what I'm doing wrong? Thank you!
EDIT:
The problem I'm having is that if there are more than one homepage in the back-links table then its counted multiple times. I need to only count each domain once.
Well, you shouldn't have to do a group by as you are not selecting anything other than aggregated fields. I'm no mysql expert, but this should work:
SELECT count(d.id) as count, sum(d.url_count) as total from domains as d
inner join backlinks as b
on b.domain_id = d.id
Where d.Link_found = 1 and b. is_homepage = 1
The reason you're getting a row for each entry in the domains table is that you're grouping by domain.id. If you want grand totals only, just leave off the GROUP BY piece.
I think a fairly simple query will do the trick:
SELECT COUNT(*), SUM(domains.URL_Count)
FROM domains
WHERE domains.link_found = 1 AND domains.id IN (
SELECT domain_id FROM backlinks WHERE is_homepage = 1)
There's a working SQLFiddle here.
Thanks for the help. Sorry it was so hard to explain I need a MySQL fiddle :)
If anyones interested heres what I ened up with:
SELECT SUM(1) AS count, SUM(total) AS total
FROM
(
SELECT SUM(`url_count`) total
FROM `domains` AS domain
LEFT JOIN `backlinks` AS link ON link.domain_id = domain.id
WHERE domain.id IN (
SELECT DISTINCT(bl.domain_id)
FROM `backlinks` AS bl
WHERE bl.tablekey_id = 11
AND bl.is_homepage = 1
)
AND domain.link_found = 1
AND link.is_homepage = 1
GROUP BY `domain`.`id`
) AS result