3 joins and where clause together - sql

I have 3 tables
bl_main (bl_id UNIQUE, bl_area)
bl_details (bl_id UNIQUE, name)
bl_data(bl_id, month, paper_tons, bottles_tons)
bl_id is not unique in the last table. There will be multiple rows of same bl_id.
I am trying to retrieve data in the following way
bl_id | name | bl_area | sum(paper_tons) | sum (bottles_tons) | paper_tons | bottles_tons
sum(paper_tons) should return the sum of all the paper tons for the same bl_id like Jan to December.
Using the below query i am able to retrieve all the data correctly except in the result, there are multiple occurances of bl_ids(From bl_data table).
SELECT bl_main.bl_id,name,bl_area,sums.SummedPaper, sums.SummedBottles,paper_tons,bottles_tons
FROM bl_main
JOIN bl_details ON
bl_main.bl_id= bl_details.bl_id
left outer JOIN bl_data ON
bl_data.bl_id= bl_main.bl_id
left outer JOIN (
SELECT bl_id, SUM(Paper_tons) As SummedPaper, SUM(bottle_tons) As SummedBottles
FROM bl_data
GROUP by bl_id) sums ON sums.bl_id = bl_main.bl_id
I wanto retrieve only the unique values of bl_ids without repetition and it should contain the bl_id which has the max month and not all the months for the same bl_id.
For ex:
INCORRECT
**0601** University Hall 75.76 17051 1356 4040 1154 **11**
**0601** University Hall 75.76 17051 1356 9190 101 **12**
**0605** UIC Student 22.86 3331 14799 0 356 **8**
CORRECT
**0601** University Hall 75.76 17051 1356 9190 101 **12**
**0605** UIC Student 22.86 3331 14799 0 356 **8**
I know I can get the max value using
WHERE Month = (SELECT MAX(Month)
but where exactlt should i add this in the query and should i change the join definition.
Any help is highly appreciated as i am new to sql. Thanks in advance.

You have two tables that probably should be combined into one (bl_main and bl_details). But putting that aside, what you need is a self-join subquery to select the row with the max month. Something like the following (untested):
SELECT bl_main.bl_id, bl_details.name, bl_main.bl_area, sums.sum_paper_tons,
sums.sum_bottles_tons, maxmonth.paper_tons, maxmonth.bottles_tons
FROM bl_main
INNER JOIN bl_details ON bl_main.bl_id = bl_details.bl_id
LEFT OUTER JOIN (SELECT bl_id, SUM(paper_tons) AS sum_paper_tons,
SUM(bottles_tons) AS sum_bottles_tons
FROM bl_data
GROUP BY bl_id) sums ON bl_main.bl_id = sums.bl_id
LEFT OUTER JOIN (SELECT bl_id, paper_tons, bottles_tons
FROM bl_data data2
INNER JOIN (SELECT bl_id, MAX(month) AS max_month
FROM bl_data
GROUP BY bl_id) m
ON m.bl_id = data2.bl_id
AND m.max_month = data2.month) maxmonth
ON bl_main.bl_id = maxmonth.bl_id

You can join the table containing the month against itself, using a subquery of the form:
Select *
From mytable m
Inner Join (Select max(Month) as Month, myId
From mytable
Group By myId) mnth
On mnth.myId = m.myId and mnth.Month = m.Month

Your JOIN clause
left outer JOIN bl_data ON
bl_data.bl_id= bl_main.bl_id
does not specify which month to select for the data you are displaying with paper_tons and bottles_tons.
You could update that JOIN to only contain the max month, and this should limit the entries, like so:
left outer JOIN (SELECT bl_id, MAX(Month) as Month from bl_data GROUP BY bl_id) as Month
ON Month.bl_id = bl_main.bl_id
left outer JOIN bl_data ON
bl_data.bl_id = bl_main.bl_id AND bl_data.Month = Month.bl_Month

I think this query is what you are looking for
SELECT bl_main.bl_id,name, bl_area, sums.SummedPaper, sums.SummedBottles, paper_tons, bottles_tons
FROM bl_main
JOIN bl_details ON bl_main.bl_id= bl_details.bl_id
left outer JOIN bl_data ON bl_data.bl_id= bl_main.bl_id
left outer JOIN
(
SELECT bl_id, month, SUM(Paper_tons) As SummedPaper, SUM(bottle_tons) As SummedBottles
FROM bl_data
WHERE month in
(SELECT MAX(month) FROM bl_data GROUP BY bl_id)
GROUP BY bl_id, month
) sums ON sums.bl_id = bl_main.bl_id

I wanted to just add a comment to the answer lc gave, but I don't have 50 reputation points yet. It is a link to an article that I believe explains this question and adds the why the solution that lc gave is correct.
http://www.sqlteam.com/article/how-to-use-group-by-with-distinct-aggregates-and-derived-tables

Related

Three tables with two counts with group by

In my query I want total file count and total closed files in the same table.
My first query:
select hi.eksper_id,ef.ad, count(hi.eksper_id) as total_files
from hasar_ihbar as hi
left outer join eksper_firma ef on ef.id=hi.eksper_id
group by hi.eksper_id,ef.ad
My second query:
select ef.id as eksper_id,ef.ad,count(ef.id) closed_files
from hasar_ihbar_rapor hir
left outer join hasar_ihbar hi on hi.id = hir.hasar_ihbar_id
left outer join eksper_firma ef on ef.id = hi.eksper_id
where hir.rapor_tipi = 3 group by ef.id,ef.ad
I want both combined and this is my code:
select ef.id as eksper_id,ef.ad,count(ef.id) closed_files, count(hi.id) AS total_files
from hasar_ihbar_rapor hir
left outer join hasar_ihbar hi on hi.id = hir.hasar_ihbar_id
left outer join eksper_firma ef on ef.id = hi.eksper_id
where hir.rapor_tipi = 3 group by ef.id,ef.ad
I don't know what I did wrong. Thanks for your help.
The double joins effect your counts since there are more rows.
A better way to combine both counts is to create a query that has two subqueries for each count.
SELECT id,
(SELECT COUNT(*)...) total_files,
(SELECT COUNT(*)...) closed_files
FROM ...

SQL query with 2 counts and 2 left outer joins

Im trying to show all columns from my t1_elem table and join 2 columns in which I use COUNT.
I used query:
SELECT p.*,COUNT(t4_id) as ile_publikacji, COUNT(t7_id) as ile_fitow
FROM t1_elem p
LEFT OUTER JOIN t4_autorzy ON p.t1_id=t4_autorzy.t4_t1_id
LEFT JOIN t7_pliki ON p.t1_id=t7_pliki.t7_t1_id
GROUP BY t1_id
But the results are bad. What I'm doing wrong?
Probably you have multiple matches. As stated, the two counts will be the same. The simplest solution is probably to use distinct:
SELECT p.*, COUNT(DISTINCT t4_id) as ile_publikacji, COUNT(DISTINCT t7_id) as ile_fitow
FROM t1_elem p LEFT JOIn
t4_autorzy
ON p.t1_id = t4_autorzy.t4_t1_id LEFT JOIN
t7_pliki
ON p.t1_id=t7_pliki.t7_t1_id
GROUP BY t1_id

How to join the results of two queries in just one table grouped by YEAR and MONTH?

I have two tables,materials_students and components_students. Both of them has afinished_at column. material_student has a component_student_id column.
I need to count the number of components_students and materials_students (Where finished_at id is not NULL), extract month and year from finished_at, group the result by month and year and plot it in just one table, like this:
| Materials | Components | Month | Year
---------------------------------------------
| 45 3 1 2019
| 37 6 2 2019
| 63 8 3 2019
I know how to do this for one table only, but dont know how to join the results in just one table.
Find below how I did for one table:
FROM materials_students
LEFT JOIN students ON materials_students.student_id = students.id
LEFT JOIN company_profiles ON students.company_profile_id = company_profiles.id
LEFT JOIN companies ON company_profiles.company_id = companies.id
WHERE materials_students.finished_at IS NOT NULL
GROUP BY YEAR, MONTH
ORDER BY YEAR, MONTH
Thanks!
The best is to assemble a subquery for each case, then join them.
select
ISNULL(M.yy, C.yy) [yy],
ISNULL(M.mm, C.mm) [mm],
ISNULL(number_material_students, 0) [number_material_students],
ISNULL(number_components_students, 0) [number_component_students]
from (
SELECT
year(materials_students.finished_at) yy,
month(materials_students.finished_at) mm,
count(*) number_material_students
FROM materials_students
LEFT JOIN students ON materials_students.student_id = students.id
LEFT JOIN company_profiles ON students.company_profile_id = company_profiles.id
LEFT JOIN companies ON company_profiles.company_id = companies.id
WHERE materials_students.finished_at IS NOT NULL
GROUP BY year(materials_students.finished_at), month(materials_students.finished_at)
) M
full outer join (
SELECT
year(components_students.finished_at) yy,
month(components_students.finished_at) mm,
count(*) number_material_students
FROM components_students
LEFT JOIN students ON components_students.student_id = students.id
LEFT JOIN company_profiles ON students.company_profile_id = company_profiles.id
LEFT JOIN companies ON company_profiles.company_id = companies.id
WHERE components_students.finished_at IS NOT NULL
GROUP BY year(materials_students.finished_at), month(materials_students.finished_at)
) C
ON C.yy = M.yy AND C.mm = M.mm
ORDER BY 1, 2
I had to make a FULL OUTER JOIN between the subqueries, because there may have been year/months that appear only on materials, but not on components, and vice-versa.
To retrieve the year I use the ISNULL() function, so in case year is not filled from the materials subquery, I use it from the components subquery. Similar reasoning applies to all other resulting columns.

SQL on joining tables (Month and subtextures that has no sales)

DBMS: Derby Embedded
Hello I wonder how I can make some outcome like
SubTextureID Year Month NetSales
1 2013 10 1000
2 2013 10 2000
3 2013 10 0
The third row never appears if that product
has no sales(no records) in the order detail table
Any help would be greatly appreciated!
Thanks
Jack
select s.TextureName, s.SubTextureId, sum(COALESCE(d.NetSales, 0)) NetSales
from (select SubTextureId, TextureName from subtexture) as s
join
(select SubTextureId, ProductCode from products) as p
on (p.SubTextureId = s.SubTextureId)
left outer join
(select ProductCode, OrderCode, NetSales from order_details) as d
on (d.ProductCode = p.ProductCode)
left outer join
( select YEAR(o.PurchaseDateTime) y,
MONTH(o.PurchaseDateTime) m,
OrderCode
from orders o
where o.PurchaseDateTime between '2013-11-01 00:00:00' and '2013-11-30 23:59:59' -- make use of an index if one exists
) as o
on (o.orderCode = d.orderCode)
group by s.TextureName, s.SubTextureId, o.y, o.m
because you used LEFT OUTER JOIN, try to use RIGHT OUTER JOIN, if you understand what's difference about LEFT and RIGHT OUTER JOIN, you will handle your problem

sql sum data from multiple tables

I have 2 tables AP and INV where both have the columns [PROJECT] and [Value].
I want a query to return something like this :
PROJECT | SUM_AP | SUM_INV
I came up with the code below but it's returning the wrong results ( sum is wrong ).
SELECT AP.[PROJECT],
SUM(AP.Value) AS SUM_AP,
SUM(INV.Value) AS SUM_INV
FROM AP INNER JOIN INV ON (AP.[PROJECT] =INV.[PROJECT])
WHERE AP.[PROJECT] = 'XXXXX'
GROUP BY AP.[PROJECT]
The results from your query are wrong because the values you are trying to summarize are being grouped, which causes duplicate values to be included in the SUM.
You could solve it with a couple of sub-selects:
SELECT
AP1.[PROJECT],
(SELECT SUM(AP2.Value) FROM AP AS AP2 WHERE AP2.PROJECT = AP1.PROJECT) AS SUM_AP,
(SELECT SUM(INV2.Value) FROM INV AS INV2 WHERE INV2.PROJECT = AP1.PROJECT) AS SUM_INV
FROM AP AS AP1
INNER JOIN INV AS INV1
ON (AP1.[PROJECT] =INV1.[PROJECT])
WHERE AP1.[PROJECT] = 'XXXXX'
GROUP BY AP1.[PROJECT]
If you have N rows in AP with a given project ID, and M rows in INV with that ID, then the join between the two tables on the project ID will have a total of N*M rows for that project, because the same row in AP will be repeated for every row in INV that has that project ID, and vice versa. Hence why your counts are most likely off (because it's counting the same row in a given table multiple times due to repetition from the join).
Instead, you might want to try doing a join between the results of two subqueries, one which groups the first table by project ID and does that its sum, and the second which groups the other table by project ID and does that sum - then joining once you only have 1 row with sum for each project ID.
If PROJECT is the parent table, you should select FROM the project table, and do a left outer join on the two child tables:
SELECT PROJECT.PROJECT_ID, SUM(AP.Value) AS SUM_AP, SUM(INV.Value) AS SUM_INV
FROM PROJECT
LEFT OUTER JOIN AP ON (AP.[PROJECT] = PROJECT.[PROJECT_ID])
LEFT OUTER JOIN INV ON (INV.[PROJECT] = PROJECT.[PROJECT_ID])
WHERE PROJECT.[PROJECT_ID] = 'XXXXX'
GROUP BY PROJECT.[PROJECT_ID]
You could separate the two sum calculations. One way I can think of is to move the inventory calculation to a subquery, like:
SELECT
AP.[PROJECT]
, SUM(AP.Value) AS SUM_AP
, SummedInv as SUM_INV
FROM AP
LEFT JOIN (
SELECT PROJECT, SUM(Value) AS SUM_INV
FROM INV
GROUP BY PROJECT
) SummedInv ON SummedInv.Project = AP.Project
GROUP BY AP.PROJECT, SummedInv.SUM_INV
Because the SummedInv subquery is grouped on project, it's safe to group on SummedInv.SUM_INV in the outer query as well.
how about this query :
select SUM(gpCutBody.actualQty) as cutQty , SUM(gpSewBody.quantity) as sewQty
from jobOrder
inner join gpCutHead on gpCutHead.joNum = jobOrder.joNum
inner join gpSewHead on gpSewHead.joNum = jobOrder.joNum
inner join gpCutBody on gpCutBody.gpCutID = gpCutHead.gpCutID
inner join gpSewBody on gpSewBody.gpSewID = gpSewHead.gpSewID
where jobOrder.joNum = '36'
here is the link to the ERD: http://dl.dropbox.com/u/18794525/AUG%207%20DUMP%20STAN.png
Try:
SELECT AP.[PROJECT] AS PROJECT, SUM(AP.[Value]) AS SUM_AP, SUM(INV.[Value]) AS SUM_INV
FROM AP, INV
WHERE AP.[PROJECT] = INV.[PROJECT]
AND AP.[PROJECT] = 'XXXXX'
GROUP BY AP.[PROJECT]