Two Left Outer Join - sql

I have two tables in this form:
Inventory:
Units InDate OutDate
1000 11/4 12/4
2000 13/4 14/4
Prices:
Date Price
11/4 5
12/4 4
13/4 6
14/4 7
I want to build the following table:
Units InDate OutDate InPrice OutPrice
1000 11/4 12/4 5 4
2000 13/4 14/4 6 7
I thought I should use something like:
Select *
FROM Inventory
LEFT OUTER JOIN Prices ON Inventory.InDate = Prices.Date
LEFT OUTER JOIN Prices ON Inventory.OutDate = Prices.Date
But the second OUTER JOIN seem to mess things up.
How can I reach this result?

Select
Units,
InDate,
OutDate,
P1.Price as InPrice,
P2.Price as OutPrice
FROM Inventory
LEFT OUTER JOIN Prices as P1 ON Inventory.InDate = P1.Date
LEFT OUTER JOIN Prices as P2 ON Inventory.OutDate = P2.Date

Try this.
SELECT Inventory.Units, Inventory.InDate, Inventory.OutDate, InPrices.Price AS InPrice, OutPrices.Price AS OutPrice
FROM Inventory
LEFT OUTER JOIN Prices AS InPrices ON Inventory.InDate = InPrices.Date
LEFT OUTER JOIN Prices AS OutPrices ON Inventory.OutDate = OutPrices.Date

Your current query was very close to being correct. If you placed different aliases on the prices table then it would have worked. Since you are joining on the same table prices twice, you need to use a different alias to distinguish between them:
select i.units,
i.indate,
i.outdate,
inPrice.price,
outPrice.price
from inventory i
left join prices inPrice -- first join with alias
on i.indate = inPrice.date
left join prices outPrice -- second join with alias
on i.outdate = outPrice.date
See SQL Fiddle with Demo

Related

SQL include entries with no data

Here is my sql statement in Oracle that I am working with:
SELECT FACILITY.fac_name
, SUM(FEE_LOG.fee_amount) AS TOTAL_FEES
FROM FACILITY
, BOOK_DETAIL
, TRANS_LOG
, FEE_LOG
, TRANS_TYPE
WHERE
FACILITY.fac_id = BOOK_DETAIL.fac_id
AND BOOK_DETAIL.bkdt_id = TRANS_LOG.bkdt_id
AND TRANS_LOG.trans_id = FEE_LOG.trans_id
AND TRANS_LOG.trans_type_id = TRANS_TYPE.trans_type_id
AND
(
TRANS_TYPE.trans_type_desc = 'LOST'
OR TRANS_TYPE.trans_type_desc = 'CHECKIN'
)
GROUP BY FACILITY.fac_name;
It outputs something similar to this:
FACILITY TOTAL_FEES
Facility 1 8.45
Facility 2 4.23
Facility 3 5.23
I have 2 other facilities but they do not have any fees associated with them. I want to show them as 0
So the output would be like:
FACILITY TOTAL_FEES
Facility 1 8.45
Facility 2 4.23
Facility 3 5.23
Facility 4 0
Facility 5 0
ER Diagram
Use LEFT JOIN instead of implicit INNER JOIN for the FEE_LOG table
SELECT FACILITY.fac_name
, SUM(FEE_LOG.fee_amount) AS TOTAL_FEES
FROM FACILITY
JOIN BOOK_DETAIL ON FACILITY.fac_id = BOOK_DETAIL.fac_id
JOIN TRANS_LOG ON BOOK_DETAIL.bkdt_id = TRANS_LOG.bkdt_id
LEFT JOIN FEE_LOG ON TRANS_LOG.trans_id = FEE_LOG.trans_id
JOIN TRANS_TYPE TRANS_LOG.trans_type_id = TRANS_TYPE.trans_type_id
WHERE TRANS_TYPE.trans_type_desc IN ('LOST', 'CHECKIN')
GROUP BY FACILITY.fac_name;
Use outer joins and the on clause:
SELECT FACILITY.fac_name, SUM(nvl(FEE_LOG.fee_amount,0)) AS TOTAL_FEES
FROM FACILITY
left join BOOK_DETAIL
on FACILITY.fac_id = BOOK_DETAIL.fac_id
left join TRANS_LOG
on BOOK_DETAIL.bkdt_id = TRANS_LOG.bkdt_id
left join FEE_LOG
on TRANS_LOG.trans_id = FEE_LOG.trans_id
left join TRANS_TYPE
on TRANS_LOG.trans_type_id = TRANS_TYPE.trans_type_id
and TRANS_TYPE.trans_type_desc in ('LOST', 'CHECKIN')
GROUP BY FACILITY.fac_name;
Note: If these facilities you are referring to, that do not have any fees, have rows on BOOK_DETAIL or TRANS_LOG you can replace the outer join with an inner join (for just those tables). Any table at which point there may or may not be a related record you have to use an outer join.
Try this:
SELECT f.fac_name
, coalesce(SUM(fl.fee_amount),0) AS TOTAL_FEES
FROM FACILITY AS f
INNER JOIN BOOK_DETAIL AS bd
ON bd.fac_id = f.fac_id
INNER JOIN TRANS_LOG AS tl
ON tl.bkdt_id = bd.bkdt_id
LEFT OUTER JOIN FEE_LOG AS fl
ON fl.trans_id = tl.trans_id
INNER JOIN TRANS_TYPE AS tt
ON tt.trans_type_id = tl.trans_type_id
WHERE tt.trans_type_desc in ('LOST' , 'CHECKIN')
GROUP BY f.fac_name;
NB: when you list tables after from with commas between them you're effectively using a cross join.
When you add a condition to your where statement matching a field from one table to a field from another, the join becomes an inner join.
To select all records from one table along with any matches which may or may not exist from another you need an outer join.
There are 3 types:
left outer join where you want all records from the first table listed and any matching records from the second.
right outer join for all records from the second with only matches from the first.
full outer join is all records from both tables, alongside one another where there's a match.
You should always specify the type of join rather than implying it through where clauses / commas, as this makes your intentions clearer and thus your code more readable.

Create View having trouble with JOINS

I have 5 regions that each contain 1 store and 1 warehouse. I have a table that contains the regions and corresponding stores and warehouses. I also have a table that lists the inventories of both the stores and warehouses. Finally, have a table that lists the allowed quantities for the warehouse. If I look at the inventory table right now it would say something like:
FACILITY ID ITEM_NUMBER QTY
STORE 1 15D 2
WAREHOUSE 1 15D 1
The problem is, Store 1 and Warehouse 1 belong to region 1 and I would like to see it in this format:
REGION WAREHOUSE QTY OH STORE QTY OH
1 1 2 1 1
and so on... So I wrote the following SQL:
Create or replace view REGION_BALANCES
as
with WAREHOUSES as
(
select
A.REGION_CODE,A.REG_DESC,A.WAREHOUSE_NUMBER,A.FACILITY_ID,C.ITEM_NUMBER,sum(C.IN_STOCK_QTY) as IN_STOCK_QTY, B.ALLOWED_QTY
from REG_WHS_STR_ASSOC A
join ALLOWANCES B on (A.FACILITY_ID = B.FACILITY_ID)
join INVENTORIES C on (A.FACILITY_ID = FACILITY_ID) and B.ITEM_NUMBER = C.ITEM_NUMBER)
group by A.REGION_CODE,A.REG_DESC,A.WAREHOUSE_NUMBER,C.ITEM_NUMBER,B.ALLOWED_QTY
),
STORES as
(
select
A.REGION_CODE,A.REG_DESC,A.STORE_NUMBER,A.FACILITY_ID,B.ITEM_NUMBER,sum(B.IN_STOCK_QTY) as IN_STOCK_QTY
from REG_WHS_STR_ASSOC A
join INVENTORIES B on (A.FACILITY_ID = B.FACILITY_ID)
group by A.REGION_CODE,A.REG_DESC,A.STORE_NUMBER, B.ITEM_NUMBER
)
select A.REGION_CODE,A.REG_DESC,A.WAREHOUSE_NUMBER,A.FACILITY_ID,WAREHOUSES.ITEM_NUMBER,WAREHOUSES.IN_STOCK_QTY,WAREHOUSES.ALLOWED_QTY,STORES.STORE_NUMBER,STORES.FACILITY_ID,STORES.ITEM_NUMBER,STORES.IN_STOCK_QTY
from REG_WHS_STR_ASSOC A
join WAREHOUSES on (A.REGION_CODE = WAREHOUSES.REGION_CODE)
join STORES on (A.REGION_CODE = STORES.REGION_CODE)
order by 5,1 asc;
I have changed my join around from left joins to right joins to cross joins to inner joins; however, I either get 30 records (and I need hundreds of records) or I get Cartesian products. Can anyone provide advice to what I'm doing wrong?
Thanks in advance!
You were forgetting to link warehouses and stores by item number (plus left join is the better for this case)
Create or replace view REGION_BALANCES
as
with WAREHOUSES as
(
select
A.REGION_CODE, A.REG_DESC, A.WAREHOUSE_NUMBER,
A.FACILITY_ID, C.ITEM_NUMBER,sum(C.IN_STOCK_QTY) as IN_STOCK_QTY, B.ALLOWED_QTY
from REG_WHS_STR_ASSOC A
join ALLOWANCES B on (A.FACILITY_ID = B.FACILITY_ID)
join INVENTORIES C on (A.FACILITY_ID = FACILITY_ID) and B.ITEM_NUMBER = C.ITEM_NUMBER)
group by A.REGION_CODE,A.REG_DESC,A.WAREHOUSE_NUMBER,C.ITEM_NUMBER,B.ALLOWED_QTY
),
STORES as
(
select
A.REGION_CODE,A.REG_DESC,A.STORE_NUMBER,
A.FACILITY_ID,B.ITEM_NUMBER,sum(B.IN_STOCK_QTY) as IN_STOCK_QTY
from REG_WHS_STR_ASSOC A
join INVENTORIES B on (A.FACILITY_ID = B.FACILITY_ID)
group by A.REGION_CODE,A.REG_DESC,A.STORE_NUMBER, B.ITEM_NUMBER
)
select A.REGION_CODE, A.REG_DESC,A. WAREHOUSE_NUMBER, A.FACILITY_ID,
WAREHOUSES.ITEM_NUMBER, WAREHOUSES.IN_STOCK_QTY, WAREHOUSES.ALLOWED_QTY,
STORES.STORE_NUMBER, STORES.FACILITY_ID, STORES.ITEM_NUMBER, STORES.IN_STOCK_QTY
from REG_WHS_STR_ASSOC A
left join WAREHOUSES on (A.REGION_CODE = WAREHOUSES.REGION_CODE)
left join STORES on
(A.REGION_CODE = STORES.REGION_CODE AND STORES.ITEM_NUMBER = WAREHOUSES.ITEM_NUMBER)
order by 5,1 asc;

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]

3 joins and where clause together

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