SUM function when using multiple tables not working right - sql

Currently I'm using the following code:
SELECT psparcomp.ps_comp,
MPA_Desc.Side,
MPA_Desc.Color_Code,
MPA_Desc.Description,
sum(ISNULL(Forecasts.schd_discr_qty, 0)) AS [Order Amount],
Product_Structure.pt_promo,
Forecasts.sod_nbr,
SUM(PaintSched.qty) AS schedamt
FROM MPA_Desc
LEFT OUTER JOIN psparcomp ON MPA_Desc.MPA_Number = psparcomp.ps_comp
LEFT OUTER JOIN Forecasts ON psparcomp.ps_par = Forecasts.sod_part
LEFT OUTER JOIN Product_Structure
ON psparcomp.ps_par = Product_Structure.pt_part
LEFT OUTER JOIN PaintSched ON MPA_Desc.MPAID = PaintSched.MPAID
WHERE Forecasts.schd_discr_qty > 0
GROUP BY psparcomp.ps_comp,
MPA_Desc.Side,
MPA_Desc.Color_Code,
MPA_Desc.Description,
ISNULL(Forecasts.schd_discr_qty, 0),
Product_Structure.pt_promo,
Forecasts.sod_nbr
And these are my results:
I need the results to just show "MPA-0856" and a total summed for the order amount. It's pulling multiple times because from the forecasts table, the part numbers it's pulling are different part numbers, but these are painted parts that are used on multiple part numbers. I'm creating a report for our paint department here and they only need to see the total amount that they need to paint instead of amount by part number. I've tried this a couple of different ways on joins but nothing seems to work. Can anyone assist?

Just guessing: it seems that your GROUP BY clause shouldn't list this item:
ISNULL(Forecasts.schd_discr_qty, 0)
as that is part of the sums made.
Also, remove from group by and select any item that could possibly split your results among different rows (eg. colour, side, etc.)

Use a Window Function. (https://www.sqlshack.com/use-window-functions-sql-server/)
SELECT psparcomp.ps_comp,
MPA_Desc.Side,
MPA_Desc.Color_Code,
MPA_Desc.Description,
sum(ISNULL(Forecasts.schd_discr_qty, 0)) OVER (PARTITION BY .... ) AS [Order Amount],
Product_Structure.pt_promo,
Forecasts.sod_nbr,
SUM(PaintSched.qty) OVER (PARTITION BY ..... ) AS schedamt
FROM MPA_Desc
LEFT OUTER JOIN psparcomp ON MPA_Desc.MPA_Number = psparcomp.ps_comp
LEFT OUTER JOIN Forecasts ON psparcomp.ps_par = Forecasts.sod_part
LEFT OUTER JOIN Product_Structure
ON psparcomp.ps_par = Product_Structure.pt_part
LEFT OUTER JOIN PaintSched ON MPA_Desc.MPAID = PaintSched.MPAID
WHERE Forecasts.schd_discr_qty > 0
GROUP BY psparcomp.ps_comp,
MPA_Desc.Side,
MPA_Desc.Color_Code,
MPA_Desc.Description,
ISNULL(Forecasts.schd_discr_qty, 0),
Product_Structure.pt_promo,
Forecasts.sod_nbr

Related

SQL sum aggregate function gives wrong results

My data is given below
Right answer is Sum = 601,050.00
But SQL sum aggregate function gives me wrong answer that is 5078150.00000
15,000.00 27,950.00 24,750.00 11,550.00 7,400.00 7,500.00 14,650.00 12,500.00 32,800.00 35,700.00 94,100.00 10,100.00 19,700.00 22,100.00 35,450.00 28,050.00 50,150.00 69,750.00 13,800.00 3,600.00 18,600.00 2,350.00 7,200.00 21,600.00 7,700.00 4,500.00 2,500.00
select sum(SO_SalesOrder.OrderTotal),l.Name as [Store Name]
From SO_SalesOrder inner join BASE_Location l on
SO_SalesOrder.LocationId = l.LocationId
inner join SO_SalesOrder_Line on SO_SalesOrder.SalesOrderId =
SO_SalesOrder_Line.SalesOrderId
inner join BASE_Product on BASE_Product.ProdId =
SO_SalesOrder_Line.ProdId
inner join BASE_Category on BASE_Category.CategoryId =
BASE_Product.CategoryId
where SO_SalesOrder.OrderDate >= '2018-02-01' and
SO_SalesOrder.OrderDate <= '2018-02-28' and BASE_Category.Name = '1MHNZ'
group by l.Name
There is likely to be a problem with one (or more) of your joins, maybe you have duplicate rows or the joining conditions are not OK.
Remove the group by l.Name, the SUM() aggregate and see if the returned values for SO_SalesOrder.OrderTotal are what you are expecting them to be (you might need to filter with a particular l.Name in a WHERE clause). It's very likely you will see duplicate amounts, or amounts you are not considering when arriving to the value 601,050.00.
If so, try joining the tables 1 by 1 and see which ones are making your rows go comando.
In my opinion your problem depends on the logic of the query.
You have a master-detail relationship between SO_SalesOrder and SO_SalesOrder_line joined by SalesOrderId column.
So if you have three lines in your order you will sum up three times the same OrderTotal.
try with something like this:
select sum(SO_SalesOrder.OrderTotal) Total, l.Name as [Store Name]
From SO_SalesOrder
join BASE_Location l on SO_SalesOrder.LocationId = l.LocationId
where SO_SalesOrder.OrderDate >= '2018-02-01' and SO_SalesOrder.OrderDate <= '28-02-2018'
and exists (
select 0 x
From SO_SalesOrder_Line
join BASE_Product on BASE_Product.ProdId = SO_SalesOrder_Line.ProdId
join BASE_Category on BASE_Category.CategoryId = BASE_Product.CategoryId
where BASE_Category.Name = '1MHNZ'
and SO_SalesOrder_Line.SalesOrderId = SO_SalesOrder.SalesOrderId
)
group by l.Name
P.S.
Check also the dates columns, if they contains also time fraction you should reconsider your upper bound filter.
I suggest you to use and SO_SalesOrder.OrderDate < '01-03-2018' instead of <= 28-02

How to get sub total and grand total Based on heading and sub heading in Sql Server

I have this SQL code
SELECT TOP (100) PERCENT
dbo._SHIFT._SHIFTNAME AS [Shift Name], dbo._TANK._TANKNAME AS [Tank Name],
dbo._NOZZLE._NOZZLENAME AS [Nozzle Name],
dbo._SHIFTENTRYDET._OPENING AS Opening, dbo._SHIFTENTRYDET._CLOSING AS Closing,
SUM(dbo._SHIFTENTRYDET._TOTALSALE) AS [Total Sale],
dbo._SHIFTENTRYDET._RATE AS Rate,
CASE
WHEN GROUPING(dbo._SHIFT._SHIFTNAME) = 1
THEN NULL
WHEN GROUPING(dbo._TANK._TANKNAME) = 1
THEN NULL
ELSE SUM(dbo._SHIFTENTRYDET._TOTALAMOUNT)
END AS Amount
FROM
dbo._NOZZLE
RIGHT OUTER JOIN
dbo._SHIFTENTRYDET ON dbo._NOZZLE._NOZZLEID_PK = dbo._SHIFTENTRYDET._NOZZLEID_PK
LEFT OUTER JOIN
dbo._TANK ON dbo._SHIFTENTRYDET._TANKID_PK = dbo._TANK._TANKID_PK
RIGHT OUTER JOIN
dbo._SHIFTENTRY ON dbo._SHIFTENTRYDET._SHIFTENTRYID_PK = dbo._SHIFTENTRY._SHIFTENTRYID_PK
LEFT OUTER JOIN
dbo._SHIFT ON dbo._SHIFTENTRY._SHIFTID_PK = dbo._SHIFT._SHIFTID_PK
WHERE
(dbo._SHIFTENTRY._ISDELETED = N'1')
AND (dbo._SHIFT._ISDELETED = N'1')
AND (dbo._SHIFTENTRYDET._ISDELETED = N'1')
GROUP BY GROUPING SETS
((dbo._SHIFT._SHIFTNAME, dbo._TANK._TANKNAME, dbo._NOZZLE._NOZZLENAME, dbo._SHIFTENTRYDET._OPENING, dbo._SHIFTENTRYDET._CLOSING, dbo._SHIFTENTRYDET._RATE), (dbo._SHIFT._SHIFTNAME, dbo._TANK._TANKNAME))
Result
but I want below result
I use GROUP BY GROUPING SETS , GROUP BY ROLLUP, GROUP BY CUBE but no luck.
If i Remove GROUP BY and GROUPING SETS Etc. the simple data will be like below
Excel file Google Drive Link : https://drive.google.com/open?id=1DgyI_7-p1l7n1KjeAnf1DerQTGLJC-wB
Excel File have 3 Sheets
Sample Data, Here the data without groupiung
Expected Result
Result With GROUPING SETS
Both of your grouping sets contain Shiftname and Tankname, so the grouping function will always return 0.
Since the WHERE clause contains criteria for Shift, Shiftentry and Shiftentrydet, the records have to exist, and so the joins are INNER JOINs (ok, maybe not for Tank and Nozzle).
It would be a better idea to group by key values and not by display names, also grouping by additional information (Opening, Closing…) seems to be wrong in my eyes. The only fields that should be grouped are the keys for Shift, Tank and Shiftentrydet (instead of Nozzle, because the Nozzles seem to make up the details), so I would do the grouping and calculation in a subquery (CTE) and join the rest in the main query, although querying the Shiftentrydet and Shift tables twice (once for calculation, once for display). I guess that a column Shiftentrydet_PK does exist).
At last, your query will not return Shift- Tank- and Nozzle-names and headings for the totals in the same column, nor will it create a header row for Shift and Tank. To achieve this, I suggest to use a report (SSRS).
For a query-only non-reporting solution, please try this (not tested):
WITH CTE_grp (SHIFTID_PK, TANKID_PK, Shipentrydet_PK, Amount) AS (
SELECT se._SHIFTID_PK, sed._TANKID_PK, sed.Shipentrydet_PK, SUM(sed._TOTALAMOUNT)
FROM dbo._SHIFT s
INNER JOIN dbo._SHIFTENTRY se ON s._SHIFTID_PK = se._SHIFTID_PK
INNER JOIN dbo._SHIFTENTRYDET sed ON se._SHIFTENTRYID_PK = sed._SHIFTENTRYID_PK
WHERE (se._ISDELETED = N'1') AND (sed._ISDELETED = N'1') AND (s._ISDELETED = N'1')
GROUP BY se._SHIFTID_PK, sed._TANKID_PK, sed.Shipentrydet_PK WITH ROLLUP
)
SELECT s._SHIFTNAME AS [Shift Name], t._TANKNAME AS [Tank Name], n._NOZZLENAME AS [Nozzle Name]
, sed._OPENING AS Opening, sed._CLOSING AS Closing, sed._TOTALSALE AS [Total Sale]
, sed._RATE AS Rate, c.Amount
FROM CTE_grp c
LEFT OUTER JOIN dbo._SHIFT s ON c.SHIFTID_PK = s._SHIFTID_PK
LEFT OUTER JOIN dbo._TANK t ON c.TANKID_PK = t._TANKID_PK
LEFT OUTER JOIN dbo._SHIFTENTRYDET sed ON c.Shipentrydet_PK = sed.Shipentrydet_PK
LEFT OUTER JOIN dbo._NOZZLE n ON sed._NOZZLEID_PK = n._NOZZLEID_PK;

LEFT JOIN in Query returning more records than desired

I'm working on an MS ACCESS query which should pull all available data from one main table (specified by FROM) and then joining three additional tables via LEFT JOIN. My issue is that the query is returning more records because of a one to many relationship.
Is there any way I can return only ONE record per line in the main table? Any help is greatly appreciated!! Thank you!
SELECT tbl_Item_PO.Item,
tbl_ItemSKUType.SKU,
tbl_Item_PO.[EX-FACTORY DATE],
tbl_Item_PO.[QTY PER SHIPMENT],
IIf(tbl_Item_PO.Item=Posted_Inv_Tran.Item And tbl_Item_PO.Whse=Posted_Inv_Tran.Whse And tbl_Item_PO.[ITR#]=Posted_Inv_Tran.[TRN Number],0,tbl_Item_PO.[QTY PER SHIPMENT]) AS QTY,
tbl_Item_PO.WHSE, tbl_WhseRegion.Region
FROM ((tbl_Item_PO
LEFT JOIN tbl_ItemSKUType ON tbl_Item_PO.item = tbl_ItemSKUType.Item)
LEFT JOIN tbl_WhseRegion ON tbl_Item_PO.WHSE = tbl_WhseRegion.Whse)
LEFT JOIN Posted_Inv_Tran ON (tbl_Item_PO.[ITR#] = Posted_Inv_Tran.[TRN Number]) AND (tbl_Item_PO.Whse = Posted_Inv_Tran.Whse) AND (tbl_Item_PO.Item = Posted_Inv_Tran.Item);
I've also tried creating sub-queries within the join statements that return only ONE record but I cannot get that to work either:
SELECT po.Item,
ist.SKU,
po.[EX-FACTORY DATE],
po.[QTY PER SHIPMENT],
IIf(po.Item=itr.Item And po.Whse=itr.Whse And po.[ITR#]=itr.[TRN Number],0,po.[QTY PER SHIPMENT]) AS QTY,
po.WHSE,
wh.Region
FROM ((tbl_Item_PO po
LEFT JOIN (select top 1 * from tbl_ItemSKUType
RIGHT JOIN tbl_Item_PO on tbl_ItemSKUType.Item = tbl_Item_PO.item
WHERE tbl_Item_PO.Item = tbl_ItemSKUType.Item)
ist ON po.item = ist.Item)
LEFT JOIN (select top 1 * from tbl_WhseRegion
RIGHT JOIN tbl_Item_PO on tbl_WhseRegion.Whse = tbl_Item_PO.Whse
WHERE tbl_Item_PO.whse = tbl_WhseRegion.Whse)
wh ON po.Whse = wh.Whse)
LEFT JOIN (select top 1 * from Posted_Inv_Tran
RIGHT JOIN tbl_Item_PO on Posted_Inv_Tran.[TRN Number] = tbl_Item_PO.[ITR#] AND Posted_Inv_Tran.Whse = tbl_Item_PO.Whse AND Posted_Inv_Tran.Item = tbl_Item_PO.Item
WHERE (tbl_Item_PO.[ITR#] = Posted_Inv_Tran.[TRN Number]) AND (tbl_Item_PO.Whse = Posted_Inv_Tran.Whse) AND (tbl_Item_PO.Item = Posted_Inv_Tran.Item))
it ON (po.[ITR#] = it.[TRN Number]) AND (po.Whse = it.Whse) AND (po.Item = it.Item);
You can achieve that with a group by clause. Make sure to list all fields in that clause which identify a line, and that all other expressions/fields you have in the select clause are aggregated with some aggregation function, like Min().
NB: Your IIf condition does not need to check all those fields, just one is enough: when one matches, all will match:
SELECT tbl_Item_PO.Item,
Min(tbl_ItemSKUType.SKU) AS SKU,
tbl_Item_PO.[EX-FACTORY DATE],
tbl_Item_PO.[QTY PER SHIPMENT],
Min(IIf(tbl_Item_PO.Item=Posted_Inv_Tran.Item, 0,
tbl_Item_PO.[QTY PER SHIPMENT])) AS QTY,
tbl_Item_PO.WHSE,
Min(tbl_WhseRegion.Region) AS Region
FROM ((tbl_Item_PO
LEFT JOIN tbl_ItemSKUType
ON tbl_Item_PO.item = tbl_ItemSKUType.Item)
LEFT JOIN tbl_WhseRegion
ON tbl_Item_PO.WHSE = tbl_WhseRegion.Whse)
LEFT JOIN Posted_Inv_Tran
ON (tbl_Item_PO.[ITR#] = Posted_Inv_Tran.[TRN Number])
AND (tbl_Item_PO.Whse = Posted_Inv_Tran.Whse)
AND (tbl_Item_PO.Item = Posted_Inv_Tran.Item)
GROUP BY tbl_Item_PO.Item,
tbl_Item_PO.[EX-FACTORY DATE],
tbl_Item_PO.[QTY PER SHIPMENT],
tbl_Item_PO.WHSE;

Using summed field in query twice with IIF statement - have I missed some syntax somewhere?

Having a bit of a problem with my code and can't figure out where I'm going wrong.
Essentially this query will return all employees for a given employer for a given year, along with the amount of their allowances, tax withheld, and gross payments they've received, and their Reportable Employer Superannuation Contributions (RESC).
RESC is any amounts (tblSuperPayments.PaymentAmount) paid over and above the superannuation guarantee, which is gross payments (sum of tblPayment.GrossPayment) * super rate (tblSuperRate.SuperRate). Otherwise, RESC is 0.
The data that I currently have in my tables is as follows
SUM(tblPayment.GrossPayment) = 1730
SUM(tblEmployee.TaxPayable) = 80
SUM(tblSuperPayments.PaymentAmount) = 500
tblSuperRate.SuperRate = 9.5%
Therefore my query should be returning an amount of RESC of 500-(1730*9.5%)= 335.65.
However, my query is currently returning $835.65 - meaning that (1730*9.5%) is returning -335.65.
I can't figure out where my logic is going wrong - it's probably something simple but I can't see it. I suspect that it might be summing tblPayment.GrossPayment twice (edited on request)
SELECT
tblEmployee.EmployeeID AS Id
SUM(tblPayment.Allowances) AS TotAllow,
SUM(tblPayment.TaxPayable) AS TotTax,
SUM(tblPayment.GrossPayment) AS TotGross,
(IIF
((SUM(tblSuperPayments.PaymentAmount)) <= (SUM(tblPayment.GrossPayment)*tblSuperRate.SuperRate),
0,
(SUM(tblSuperPayments.PaymentAmount) - (SUM(tblPayment.GrossPayment)*tblSuperRate.SuperRate))
)) As TotRESC
FROM
((tblEmployee
LEFT JOIN tblPayment // any reason for using left join over inner join
ON tblEmployee.EmployeeID = tblPayment.fk_EmployeeID)
LEFT JOIN tblSuperPayments // any reason for using left join over inner join
ON tblEmployee.EmployeeID = tblSuperPayments.fk_EmployeeID)
LEFT JOIN tblSuperRate // any reason for using left join over inner join
ON (tblPayment.PaymentDate <= tblSuperRate.TaxYearEnd) // these two conditions might be returning
AND (tblPayment.PaymentDate >= tblSuperRate.TaxYearStart) //two SuperRate rows because of using equals in both
WHERE
tblEmployee.fk_EmployerID = 1
GROUP BY
tblEmployee.EmployeeID,
tblSuperRate.SuperRate;
Looking at your query I recommend you to just group by primary key (EmployeeID) of tblEmployee and the use the result as a sub query and do a join later tham using many columns of tblEmployeein group by which might cause duplicate rows. I rewrote the query as I have mentioned above and added comments at places which might cause the error.
SELECT
tblEmployee.TFN,
tblEmployee.FirstName,
tblEmployee.MiddleName,
tblEmployee.LastName,
tblEmployee.DOB,
tblEmployee.MailingAddress,
tblEmployee.AddressLine2,
tblEmployee.City,
tblEmployee.fk_StateProvinceID,
tblEmployee.PostalCode,
temp.TotAllow,
temp.TotTax,
temp.TotGross,
temp.TotRESC
FROM
(SELECT
tblEmployee.EmployeeID AS Id
SUM(tblPayment.Allowances) AS TotAllow,
SUM(tblPayment.TaxPayable) AS TotTax,
SUM(tblPayment.GrossPayment) AS TotGross,
(IIF
((SUM(tblSuperPayments.PaymentAmount)) <= (SUM(tblPayment.GrossPayment)*tblSuperRate.SuperRate),
0,
(SUM(tblSuperPayments.PaymentAmount) - (SUM(tblPayment.GrossPayment)*tblSuperRate.SuperRate))
)) As TotRESC
FROM
((tblEmployee
LEFT JOIN tblPayment // any reason for using left join over inner join
ON tblEmployee.EmployeeID = tblPayment.fk_EmployeeID)
LEFT JOIN tblSuperPayments // any reason for using left join over inner join
ON tblEmployee.EmployeeID = tblSuperPayments.fk_EmployeeID)
LEFT JOIN tblSuperRate // any reason for using left join over inner join
ON (tblPayment.PaymentDate <= tblSuperRate.TaxYearEnd) // these two conditions might be returning
AND (tblPayment.PaymentDate >= tblSuperRate.TaxYearStart) //two SuperRate rows because of using equals in both
WHERE
tblEmployee.fk_EmployerID = 1
GROUP BY
tblEmployee.EmployeeID,
tblSuperRate.SuperRate) temp // Does a single employee have more than one superrate why grouping by it?
JOIN tblEmployee ON tblEmployee.EmployeeID=temp.Id;

Joining SQL tables to compare revenue vs expense

Let me say first that I'm new to SQL, and learning much every day. With that said, here is my problem. I have a view that is already created (It shows revenue generated on equipment), but I need one more table added to it (Expenses against the equipment). When I try to add an inner join table, it create a bunch of duplicate views. Here is my original view (For the revenue portion of it):
SELECT
<removed, there are about 25 of them>
FROM
dbo.LRCON WITH (nolock)
INNER JOIN dbo.LRCONVIN WITH (nolock) ON dbo.LRCONVIN.ConId = dbo.LRCON.ConId
INNER JOIN dbo.LRBILCON WITH (nolock) ON dbo.LRBILCON.ConId = dbo.LRCONVIN.ConId AND dbo.LRBILCON.UntId = dbo.LRCONVIN.UntId
INNER JOIN dbo.LRBILITM WITH (nolock) ON dbo.LRBILITM.ParentItmId = dbo.LRBILCON.ItmId
INNER JOIN dbo.LRBIL WITH (nolock) ON dbo.LRBIL.BilId = dbo.LRBILCON.BilId
INNER JOIN dbo.LRCONTYP WITH (nolock) ON dbo.LRCONTYP.ConTypId = dbo.LRCON.ConTypId
INNER JOIN dbo.COLOOKUP AS C1 WITH (nolock) ON C1.Id = dbo.LRBILITM.ItmTyp
INNER JOIN dbo.COLOOKUP AS C2 WITH (nolock) ON C2.Id = dbo.LRCONTYP.ConTyp
INNER JOIN dbo.VHVIN WITH (nolock) ON dbo.VHVIN.UntId = dbo.LRCONVIN.UntId
WHERE
(dbo.LRBIL.Status = 647) AND (dbo.LRBILITM.ItmTyp <> 274)
I then try to add another join:
INNER JOIN dbo.SVSLS WITH (nolock) on dbo.SVSLS.UntId = dbo.LRCONVIN.UntId
with the select statement:
ROUND(dbo.SVSLS.AmtSubtotal + dbo.SVSLS.AmtSupplies + dbo.SVSLS.AmtDiagnostic + dbo.SVSLS.AmtTax1 + dbo.SVSLS.AmtTax2, 2) AS SvcAmtSale
... but it produces many, many rows of duplicates because it adds the detail of each expense to each row of my original table.
Original table:
https://dl.dropboxusercontent.com/u/81145403/orginal_table.jpg
After I add my new join/select:
https://dl.dropboxusercontent.com/u/81145403/failed_table.jpg
How do I fix this? At the end of the day, I just want to compare my revenue vs expenses on equipment over a date range. I really don't care to have the individual detail of the expenses, just a grand total is fine with me.
Is the SvcSaleAmt the revenue that you are interested in? And are the multiple detail rows separate entries on the same item? If you do not necessarily care about the individual details, you can GROUP the items together. In order to do this, you will need to get rid of the SlsId from your SELECT list, and add
GROUP BY CusId, CusName, BillId, ConId, Prd, ConTypId, ....., AmtCos, AmtGpm
Using all of the columns you have in your SELECT statement. Replace the ROUND() AS SvcSaleAmt with:
ROUND(SUM(dbo.SVSLS.AmtSubtotal + dbo.SVSLS.AmtSupplies + dbo.SVSLS.AmtDiagnostic + dbo.SVSLS.AmtTax1 + dbo.SVSLS.AmtTax2), 2) AS SvcSaleAmt