Expand Join to not limit data - sql

I have a weird question - I understand that Joins return matching data based on the 'ON' stipulation, however the problem I am facing is I need the Business date back for both tables but at the same time i need to join on the date in order to get the totals correct
See below code:
Select
o.Resort,
o.Business_Date,
Occupied,
Comps,
House,
ADR,
Room_Revenue,
Occupied-(Comps+House) AS DandT,
Coalesce(gd.Projected_Occ1,0) AS Projected_Occ1,
Occupied-(Comps+House)+Coalesce(gd.Projected_Occ1,0) as Total
from Occupancy o
left join Group_Details_HF gd
on o.Business_Date = gd.Business_Date
and o.Resort = gd.resort
UNION ALL
select
o.Resort,
o.Business_Date,
Occupied,
Comps,
House,
ADR,
Room_Revenue,
Occupied-(Comps+House) AS DandT,
Coalesce(gd.Projected_Occ1,0) AS Projected_Occ1,
Coalesce(Occupied-(Comps+House),0)+Coalesce(gd.Projected_Occ1,0) as Total
from Occupancy_Forecast o
FULL OUTER JOIN Group_Details_HF gd
on o.Business_Date = gd.Business_Date
and o.Resort = gd.resort
Currently, this gives me the desired results from the Occupancy and Occupancy forecast table however when the business date does not exist in the occupancy forecast table it ignores the group_details table, I need the results to combine the dates when they exist in both or give the unique results for each when there is no match

I have decided to create another pivot table storing the details from Group_Details_HF and then Union together the two tables which has given me the desired result rather than fiddling with the join :)

Related

Left join + aggregate functions resulting in incorrect values

It feels like there is a very simple solution but I am new to SQL and have been struggling with this for a while.
I am working on joining two tables so that I can map the cost center in one to its associated product group in another. There is a parent child relationship here with product group as parent and cost center as child.
I am ultimately trying to see the spend data, currently only available by cost center in table 1, by the parent product group categorization. Prior to the join, I see the correct dollar value by cost center. After the join, the number dramatically increases and is incorrect.
Table 1 (purchase orders): cost_center_id, amount_ordered
Table 2 (employee plus): cost center_id, product_group_name
Below is a simplified sample of the query I am working with.
SELECT
po.po_cost_center_id,
ep.product_group_name,
SUM(po.amount_ordered)
FROM purchase_orders po
LEFT JOIN d_employee_plus ep on po.cost_center_id = ep.cost_center_id and ep.ds =
po.ds
WHERE
po.ds = (select max(ds) from purchase_orders)
GROUP BY 1,2
My take here is that you have duplicate rows because you're not aggregating on both the columns you're joining on. I may be wrong, though try using the following:
SELECT po.po_cost_center_id,
ep.ds,
ep.product_group_name,
SUM(po.amount_ordered)
FROM purchase_orders po
LEFT JOIN d_employee_plus ep
ON po.cost_center_id = ep.cost_center_id
AND ep.ds = po.ds
WHERE po.ds = (SELECT max(ds) FROM purchase_orders)
GROUP BY po.po_cost_center_id,
ep.ds,
ep.product_group_name
For more troubleshooting, if you can provide some sample data, I may help you further.
Does it work for you?

Calculate differences between two columns of two different tables

I want to calculate the difference between purchase order amount and purchase invoice amount. But I am not able to fetch the purchase invoice amount i.e. "pi.grand_total" and hence also not able to fetch the difference of "(pi.grand_total - po.grand_total)" . Please help.Below is my query.
PO = Purchase Order
PI = Purchase Invoice
SELECT DISTINCT
po.name AS "PO #:Link/Purchase Order:120",
po.supplier AS "Supplier:Link/Supplier:120",
po.Company AS "Company:Data:120",
po.currency AS "Currency:Link/Currency:120",
po.base_grand_total AS "Grand Total:Currency:120",
po.status AS "Status:Data:120",
po.per_received AS "Per Received:Data:120",
CEILING(po.per_billed) AS "Per Billed:Data:120",
po.delivery_date AS "Delivery Date:Date:120",
pi.grand_total AS "Final PI Total:120",
(pi.grand_total - po.grand_total) AS "Amount Difference:120"
FROM
"tabPurchase Order" as po
LEFT JOIN "tabPurchase Invoice" as pi ON po.name = pi.parent
WHERE
po.per_received = 100
AND
CEILING(po.per_billed) < 100
ORDER BY po.delivery_date ASC
You are using LEFT JOIN. This means when your second table has no data which matches with your first table you will receive no data from second table but nonetheless your first table will return all of its data.
You probably should check your join condition. If you wanted to join these two tables the way you want then use NVL Function for pi.grand_total column. Because it is on the left join it could have a NULL value thus NULL minus po.grand_total will give you NULL. NVL function turns NULL values into intended values like NVL(pi.grand_total,0)
A good example how joins work

SQL Inner Join Combining Rows in Results - Too Few Results

I'm trying to create a report from four tables: Master, Details, Labor, Costs. Currently I have a working report pulling columns from Jobs, Details, and Costs. Shared key for all tables is JobNumber. I'm looking to get a sum of the costs and a sum of the hours for each job. Here is my code that works with three tables:
SELECT
JC_JobMaster.JobNumber,
JC_JobMaster.JobDescription1 AS Customer_Name,
JC_JobMaster.JobDescription2 AS Work,
JC_JobSortCategories.SortCategory2 AS Work_Type,
JC_JobMaster.CustomerCode,
JC_JobMaster.ContractValue,
SUM(JC_JobBalancesCostDollars.CurrentPeriodAmount*-1) AS Gross_profit,
JC_JobSortCategories.SortCategory1 AS City,
JC_JobSortCategories.SortCategory4 AS Salesman
FROM
JC_JobMaster INNER JOIN
JC_JobSortCategories ON JC_JobMaster.JobNumber = JC_JobSortCategories.JobNumber INNER JOIN
JC_JobBalancesCostDollars ON JC_JobMaster.JobNumber = JC_JobBalancesCostDollars.JobNumber
WHERE
JC_JobMaster.JobNumber between '11566' and '13441' and
JC_JobSortCategories.SortCategory5 = 'AWARDED' and
JC_JobSortCategories.SortCategory2 = 'FPR' and
JC_JobSortCategories.SortCategory1 in ('1')
Group By
JC_JobMaster.JobNumber,
JC_JobMaster.JobDescription1,
JC_JobMaster.JobDescription2,
JC_JobSortCategories.SortCategory2,
JC_JobMaster.CustomerCode,
JC_JobMaster.ContractValue,
JC_JobSortCategories.SortCategory1,
JC_JobSortCategories.SortCategory4
Order by
JC_JobMaster.JobNumber
Everything works fine - this query returns 45 rows with easily-verified data. However, adding in a fourth table using an inner join INNER JOIN JC_JobCostDetailLabourHours ON JC_JobMaster.JobNumber = JC_JobCostDetailLabourHours.JobNumber only returns 7 rows, and the summed column Gross_profit is incorrect. Using a LEFT JOIN as opposed to INNER JOIN gives me the proper 45 rows, but the summed column is still incorrect. The LabourHours and BalancesCostDollars tables have multiple entries for each job number that I'm wanting summed. What am I doing wrong with adding the fourth table, or did I just happen to get the right result by accident with the first summed column?

Access 2013 SQL, three tables, two using sum wrong results

Can someone please help me with this issue? I've scoured the Internet looking at dozens of examples, but i just can't find a solution that works.
I am using Access 2013. The problem is that I am trying to make a query that will highlight all part numbers from a supplier that either has customer back orders and/or overdue deliveries.
I am using three tables:
tbl_Inventory_Master which I require the part number, on hand stock value, and the supplier code.
For any back orders I need to join the tbl_Customer_Back_Order table as I need the count of back order lines and the sum of the back order quantity.
If the supplier has a late delivery, then I need to add the tbl_On_Order table showing the count of overdue deliveries and the sum of the overdue quantities.
The query is retrieving the data but the returned quantities are double what they should be.
SELECT
I.Inventory_Part_Num, I.Description, I.On_Hand_Stock,
COUNT (B.Part_Number) AS Back_Order_Count, SUM(B.Back_Order_Qty) as BO_Qty,
COUNT(O.Part_Number) AS Late_Deliveries_Count, SUM(O.Order_Qty) AS Late_Qty
FROM (tbl_Inventory_Master AS I
LEFT OUTER JOIN tbl_Customer_Back_Order AS B
ON I.Inventory_Part_Num = B.Part_Number)
LEFT OUTER tbl_On_Order AS O
ON I.Inventory_Part_Num = O.Part_Number
WHERE
I.Customer_Code = '274' AND
O.Due_Date < [ENTER TODAYS DATE IN FORMAT DD/MM/YYYY]
GROUP BY I.Inventory_Part_Num, I.Description, I.On_Hand_Stock
For example, for the part number 2022940 I should have 10 back order lines and an overdue quantity of 43. Instead, the query is returning 20 back order lines and an overdue quantity sum of 86.
From the on order table I have three orders totaling 144 pieces, instead the query is returning 960.
Can someone please advise, as this is driving me crazy?
You are joining along unrelated dimensions, so you need to aggregate before joining:
SELECT I.Inventory_Part_Num, I.Description, I.On_Hand_Stock,
B.Back_Order_Count, B.BO_Qty,
O.Late_Deliveries_Count, O.Late_Qty
FROM (tbl_Inventory_Master AS I LEFT OUTER JOIN
(SELECT B.Part_Number, COUNT(*) as Back_Order_Count,
SUM(B.Back_Order_Qty) as BO_Qty
FROM tbl_Customer_Back_Order AS B
GROUP BY B.Part_Number
) as B
ON I.Inventory_Part_Num = B.Part_Number
) LEFT JOIN
(SELECT O.Part_Number, COUNT(O.Part_Number) AS Late_Deliveries_Count,
SUM(O.Order_Qty) AS Late_Qty
FROM tbl_On_Order AS O
WHERE O.Due_Date < [ENTER TODAYS DATE IN FORMAT DD/MM/YYYY]
GROUP BY O.Part_Number
) as O
ON I.Inventory_Part_Num = O.Part_Number
WHERE I.Customer_Code = '274';
Notice the outer aggregation is no longer needed.

Include missing years in Group By query

I am fairly new in Access and SQL programming. I am trying to do the following:
Sum(SO_SalesOrderPaymentHistoryLineT.Amount) AS [Sum Of PaymentPerYear]
and group by year even when there is no amount in some of the years. I would like to have these years listed as well for a report with charts. I'm not certain if this is possible, but every bit of help is appreciated.
My code so far is as follows:
SELECT
Base_CustomerT.SalesRep,
SO_SalesOrderT.CustomerId,
Base_CustomerT.Customer,
SO_SalesOrderPaymentHistoryLineT.DatePaid,
Sum(SO_SalesOrderPaymentHistoryLineT.Amount) AS [Sum Of PaymentPerYear]
FROM
Base_CustomerT
INNER JOIN (
SO_SalesOrderPaymentHistoryLineT
INNER JOIN SO_SalesOrderT
ON SO_SalesOrderPaymentHistoryLineT.SalesOrderId = SO_SalesOrderT.SalesOrderId
) ON Base_CustomerT.CustomerId = SO_SalesOrderT.CustomerId
GROUP BY
Base_CustomerT.SalesRep,
SO_SalesOrderT.CustomerId,
Base_CustomerT.Customer,
SO_SalesOrderPaymentHistoryLineT.DatePaid,
SO_SalesOrderPaymentHistoryLineT.PaymentType,
Base_CustomerT.IsActive
HAVING
(((SO_SalesOrderPaymentHistoryLineT.PaymentType)=1)
AND ((Base_CustomerT.IsActive)=Yes))
ORDER BY
Base_CustomerT.SalesRep,
Base_CustomerT.Customer;
You need another table with all years listed -- you can create this on the fly or have one in the db... join from that. So if you had a table called alltheyears with a column called y that just listed the years then you could use code like this:
WITH minmax as
(
select min(year(SO_SalesOrderPaymentHistoryLineT.DatePaid) as minyear,
max(year(SO_SalesOrderPaymentHistoryLineT.DatePaid) as maxyear)
from SalesOrderPaymentHistoryLineT
), yearsused as
(
select y
from alltheyears, minmax
where alltheyears.y >= minyear and alltheyears.y <= maxyear
)
select *
from yearsused
join ( -- your query above goes here! -- ) T
ON year(T.SO_SalesOrderPaymentHistoryLineT.DatePaid) = yearsused.y
You need a data source that will provide the year numbers. You cannot manufacture them out of thin air. Supposing you had a table Interesting_year with a single column year, populated, say, with every distinct integer between 2000 and 2050, you could do something like this:
SELECT
base.SalesRep,
base.CustomerId,
base.Customer,
base.year,
Sum(NZ(data.Amount)) AS [Sum Of PaymentPerYear]
FROM
(SELECT * FROM Base_CustomerT INNER JOIN Year) AS base
LEFT JOIN
(SELECT * FROM
SO_SalesOrderT
INNER JOIN SO_SalesOrderPaymentHistoryLineT
ON (SO_SalesOrderPaymentHistoryLineT.SalesOrderId = SO_SalesOrderT.SalesOrderId)
) AS data
ON ((base.CustomerId = data.CustomerId)
AND (base.year = Year(data.DatePaid))),
WHERE
(data.PaymentType = 1)
AND (base.IsActive = Yes)
AND (base.year BETWEEN
(SELECT Min(year(DatePaid) FROM SO_SalesOrderPaymentHistoryLineT)
AND (SELECT Max(year(DatePaid) FROM SO_SalesOrderPaymentHistoryLineT))
GROUP BY
base.SalesRep,
base.CustomerId,
base.Customer,
base.year,
ORDER BY
base.SalesRep,
base.Customer;
Note the following:
The revised query first forms the Cartesian product of BaseCustomerT with Interesting_year in order to have base customer data associated with each year (this is sometimes called a CROSS JOIN, but it's the same thing as an INNER JOIN with no join predicate, which is what Access requires)
In order to have result rows for years with no payments, you must perform an outer join (in this case a LEFT JOIN). Where a (base customer, year) combination has no associated orders, the rest of the columns of the join result will be NULL.
I'm selecting the CustomerId from Base_CustomerT because you would sometimes get a NULL if you selected from SO_SalesOrderT as in the starting query
I'm using the Access Nz() function to convert NULL payment amounts to 0 (from rows corresponding to years with no payments)
I converted your HAVING clause to a WHERE clause. That's semantically equivalent in this particular case, and it will be more efficient because the WHERE filter is applied before groups are formed, and because it allows some columns to be omitted from the GROUP BY clause.
Following Hogan's example, I filter out data for years outside the overall range covered by your data. Alternatively, you could achieve the same effect without that filter condition and its subqueries by ensuring that table Intersting_year contains only the year numbers for which you want results.
Update: modified the query to a different, but logically equivalent "something like this" that I hope Access will like better. Aside from adding a bunch of parentheses, the main difference is making both the left and the right operand of the LEFT JOIN into a subquery. That's consistent with the consensus recommendation for resolving Access "ambiguous outer join" errors.
Thank you John for your help. I found a solution which works for me. It looks quiet different but I learned a lot out of it. If you are interested here is how it looks now.
SELECT DISTINCTROW
Base_Customer_RevenueYearQ.SalesRep,
Base_Customer_RevenueYearQ.CustomerId,
Base_Customer_RevenueYearQ.Customer,
Base_Customer_RevenueYearQ.RevenueYear,
CustomerPaymentPerYearQ.[Sum Of PaymentPerYear]
FROM
Base_Customer_RevenueYearQ
LEFT JOIN CustomerPaymentPerYearQ
ON (Base_Customer_RevenueYearQ.RevenueYear = CustomerPaymentPerYearQ.[RevenueYear])
AND (Base_Customer_RevenueYearQ.CustomerId = CustomerPaymentPerYearQ.CustomerId)
GROUP BY
Base_Customer_RevenueYearQ.SalesRep,
Base_Customer_RevenueYearQ.CustomerId,
Base_Customer_RevenueYearQ.Customer,
Base_Customer_RevenueYearQ.RevenueYear,
CustomerPaymentPerYearQ.[Sum Of PaymentPerYear]
;