I have a problem with left join, sum(), group by - sql

I have 4 tables in the database
dbo.Product_Tbl
dbo.Buy_Details_Tbl
R_BuyDetails_Tbl
Sale_Details_Tbl
Re_Sale_Details_Tbl
They all have a one-to-many relationship with the first table. I am trying to get the sum of the quantities sold and bought, the sum total amount sold and bought, the sum quantities returned bought and sold, and the sum total amount returned bought and sold in the tables above
The problem is that this sentence gives a wrong sum. Sometimes you double the sum to two or three times.
This is the sentence that I'm trying to find the true solution but it is wrong sum.
SELECT dbo.Product_Tbl.Prd_ID, dbo.Product_Tbl.Prd_Code, dbo.Product_Tbl.Prd_Name, dbo.Cat_Tbl.CatName,
dbo.Unit_Tbl.UnitName, sum(isnull(dbo.Buy_Details_Tbl.Qty,0))as 'sumBuyQty', sum(isnull(dbo.Buy_Details_Tbl.TotalAmount,0))as 'sumBuyTotalAmount',
sum(isnull(dbo.R_BuyDetails_Tbl.Qty,0)) AS 'sumR_BuyQty',sum(isnull(dbo.R_BuyDetails_Tbl.TotalAmount,0))as 'sumR_BuyTotalAmount',
sum (isnull(dbo.Sale_Details_Tbl.Qty,0)) AS 'sumSaleQty',sum(isnull( dbo.Sale_Details_Tbl.TotalAmount,0)) AS'sumSaleTotalAmount',
sum(isnull( dbo.Re_Sale_Details_Tbl.Qty,0)) As'sumRe_SaleQty',sum( isnull(dbo.Re_Sale_Details_Tbl.TotalAmount,0)) AS 'sumRe_SaleTotalAmount'
FROM dbo.Product_Tbl left JOIN dbo.Buy_Details_Tbl ON
dbo.Product_Tbl.Prd_ID = dbo.Buy_Details_Tbl.Prd_ID left JOIN
dbo.R_BuyDetails_Tbl ON
dbo.Product_Tbl.Prd_ID = dbo.R_BuyDetails_Tbl.Prd_ID left JOIN
dbo.Re_Sale_Details_Tbl ON
dbo.Product_Tbl.Prd_ID = dbo.Re_Sale_Details_Tbl.Prd_ID left JOIN
dbo.Sale_Details_Tbl ON
dbo.Product_Tbl.Prd_ID = dbo.Sale_Details_Tbl.Prd_ID left JOIN
dbo.Cat_Tbl ON
dbo.Product_Tbl.Cat_ID = dbo.Cat_Tbl.Cat_ID left JOIN
dbo.Unit_Tbl ON
dbo.Product_Tbl.Unit_ID = dbo.Unit_Tbl.Unit_ID group by dbo.Product_Tbl.Prd_ID, dbo.Product_Tbl.Prd_Code, dbo.Product_Tbl.Prd_Name, dbo.Cat_Tbl.CatName , dbo.Unit_Tbl.UnitName

Rewriting your script using subqueries would look something like this:
SELECT
p.Prd_ID,
p.Prd_Code,
p.Prd_Name,
c.CatName,
u.UnitName,
(SELECT SUM(bd.Qty) FROM dbo.Buy_Details_Tbl bd WHERE bd.Prd_ID = p.Prd_ID) AS sumBuyQty,
(SELECT SUM(bd.TotalAmount) FROM dbo.Buy_Details_Tbl bd WHERE bd.Prd_ID = p.Prd_ID) AS sumBuyTotalAmount,
(SELECT SUM(rbd.Qty) FROM dbo.R_BuyDetails_Tbl rbd WHERE rbd.Prd_ID = p.Prd_ID) AS sumR_BuyQty,
(SELECT SUM(rbd.TotalAmount) FROM dbo.R_BuyDetails_Tbl rbd WHERE rbd.Prd_ID = p.Prd_ID) AS sumR_BuyTotalAmount,
(SELECT SUM(sd.Qty) FROM dbo.Sale_Details_Tbl sd WHERE sd.Prd_ID = p.Prd_ID) AS sumSaleQty,
(SELECT SUM(sd.TotalAmount) FROM dbo.Sale_Details_Tbl sd WHERE sd.Prd_ID = p.Prd_ID) AS sumSaleTotalAmount,
(SELECT SUM(rsd.Qty) FROM dbo.Re_Sale_Details_Tbl rsd WHERE rsd.Prd_ID = p.Prd_ID) AS sumRe_SaleQty,
(SELECT SUM(rsd.TotalAmount) FROM dbo.Re_Sale_Details_Tbl rsd WHERE rsd.Prd_ID = p.Prd_ID) AS sumRe_SaleTotalAmount
FROM dbo.Product_Tbl p
INNER JOIN dbo.Cat_Tbl c ON p.Cat_ID = c.Cat_ID
INNER JOIN dbo.Unit_Tbl u ON p.Unit_ID = u.Unit_ID
This query eliminates the multiple joins to the details tables.
The isnull function shouldn't be necessary since aggregrate functions such as SUM won't count nulls.

Related

How to subtract from another table in SQL

SELECT
COUNT(ca.Plate) as 'OccupiedElectricSlots'
FROM cities C
JOIN ParkingHouses HS on C.Id = hs.CityId
JOIN ParkingSlots PS on HS.Id = ps.ParkingHouseId
LEFT JOIN Cars Ca on PS.Id = Ca.ParkingSlotsId
WHERE ps.ElectricOutlet = 1
GROUP BY hs.HouseName, C.CityName
SELECT
MAX(Ps.SlotNumber) as 'ParkingSlotTotal'
,MAX(PS.SlotNumber) - Count(ca.Plate) as 'FreeSlots'
,SUM(CAST(PS.ElectricOutlet AS INT)) as 'ElectricOutlet'
,Hs.HouseName
,C.CityName
FROM Cities C
JOIN ParkingHouses HS on C.Id = hs.CityId
JOIN ParkingSlots PS on HS.Id = ps.ParkingHouseId
LEFT JOIN Cars Ca on PS.Id = Ca.ParkingSlotsId
GROUP BY hs.HouseName, C.CityName
How can I subtract the first tables numbers on the second one?
I want to see how many free slots that have electric outlet.
Like this Column ElectricOutlet - OccupiedElectricSlots = result
I'm quite new at SQL, but I have tried to outer apply (don't fully understand it), and I tried to join them both tables togheter. Tried different where conditions but I'm stuck atm.
Your queries are almost identical as far as I can see. You can change your first query to:
SELECT COUNT(CASE WHEN ps.ElectricOutlet = 1 THEN ca.Plate END) as 'OccupiedElectricSlots'
FROM cities C
JOIN ParkingHouses HS on C.Id = hs.CityId
JOIN ParkingSlots PS on HS.Id = ps.ParkingHouseId
LEFT JOIN Cars Ca on PS.Id = Ca.ParkingSlotsId
GROUP BY hs.HouseName, C.CityName
I.e., instead of filtering on ps.ElectricOutlet you just ignore those rows in COUNT. Now you can just:
SELECT
[...]
,SUM(CAST(PS.ElectricOutlet AS INT)) - COUNT(CASE WHEN ...) AS result
[...]
FROM Cities C
JOIN ParkingHouses HS
ON C.Id = hs.CityId
JOIN ParkingSlots PS
ON HS.Id = ps.ParkingHouseId
LEFT JOIN Cars Ca
ON PS.Id = Ca.ParkingSlotsId
GROUP BY hs.HouseName, C.CityName
The MINUS operator is used to subtract the result set obtained by first SELECT query from the result set obtained by second SELECT query.
MINUS compares the data in two tables and returns only the rows of data using the specified columns that exist in the first table but not the second.

How to Sum Entire Column with SQL

I'm trying to Sum an entire column in SQL. The code below sums each row, but instead I just want a total row. My guess is it has to be done with GROUP BY, but the only way the query works is if I use Share_Type or Balance, neither of which sums the column. I also tried adding the CASE statement to the Group by (by leaving off 'AS MoneyMaxBalance" but I get an error message.
SELECT
CASE
WHEN SHARE_TYPE = 57 THEN SUM(BALANCE) ELSE 0
END AS MoneyMaxBalance
FROM SHARE
INNER JOIN ACCOUNT ON SHARE.MEMBER_NBR = ACCOUNT.MEMBER_NBR AND
SHARE.SHARE_NBR = ACCOUNT.ACCOUNT_NBR
INNER JOIN PRODUCT ON ACCOUNT.PRODUCT_CODE = PRODUCT.PRODUCT_CODE
GROUP BY SHARE_TYPE
If you just want one row, don't use group by:
SELECT SUM(BALANCE) AS MoneyMaxBalance
FROM SHARE s INNER JOIN
ACCOUNT a
ON s.MEMBER_NBR = a.MEMBER_NBR AND
s.SHARE_NBR = a.ACCOUNT_NBR INNER JOIN
PRODUCT p
ON a.PRODUCT_CODE = p.PRODUCT_CODE
WHERE SHARE_TYPE = 57;
SELECT SUM(CASE WHEN SHARE_TYPE = 57 THEN BALANCE ELSE 0 END) AS MoneyMaxBalance
FROM SHARE s
INNER JOIN ACCOUNT a ON s.MEMBER_NBR = a.MEMBER_NBR AND s.SHARE_NBR = a.ACCOUNT_NBR
INNER JOIN PRODUCT p ON a.PRODUCT_CODE = p.PRODUCT_CODE
Hope this Query Works fine for your case:

Pulling cost field from a sub-query

I wrote the following query to pull a unit cost from another table COSreport into my profitability query ProfitabilityReport and am having a problem with my sub-query.
select
i.tranid
, it.item_id
, it.displayname
, tl.Item_Count * -1 Unit_Qty
, case when tl.Item_Count=0 then 0
else ((tl.GROSS_AMOUNT * -1)/ Item_Count) * -1
end as PricePerUnit,
**(select sum(c.tranamt) from ns.COSreport c
inner join ns.ProfitabilityReport d
on c.InvoiceID = d.tranid
and c.item_id = d.item_id) as 'True Cost'**
, '0' 'Cost Per M'
from ns.tinvoice i
join ns.transaction_lines tl on i.transaction_id = tl.transaction_id
join ns.Customers cust on c.customer_id = i.ENTITY_ID
join ns.items it on it.item_id = tl.item_id
left join ns.ITEM_CLASSIFICATION it_class on it_class.list_id =
it.ITEM_CLASSIFICATION_ID
where list_item_name IS NOT NULL
and i.tranid = '1262INV'
I'm joining on both the invoice id and item id so that the proper cost is pulled across for the given invoice and item from COSReport.
However, the true cost is not coming up with the unit cost but instead is summing up the cost field for the entire table.
See below for example using invoice # 1262INV specified in the query above. The cost should be 1.04, .26, and 4 respectively vs 138m.
Any help getting this cleared up would be appreciated
I actually prefer using CTEs for readability. You can take your subquery, put it into a CTE, and then join it in your main query, but you'll want to add the tranid and item_id fields to the CTE so you can use them in your join.
EDIT: since you're using Azure SQL Server, you don't need the semicolon before the WITH.
WITH TrueCosts AS
(
SELECT
d.tranid
,d.item_id
,TrueCost = SUM(c.tranamt)
FROM ns.COSreport c
INNER JOIN ns.ProfitabilityReport d
ON c.InvoiceID = d.tranid
AND c.item_id = d.item_id
GROUP BY d.tranid
,d.item_id
)
SELECT
i.tranid
, it.item_id
, it.displayname
, tl.Item_Count * -1 Unit_Qty
, case when tl.Item_Count=0 then 0
else ((tl.GROSS_AMOUNT * -1)/ Item_Count) * -1
END as PricePerUnit
, tc.TrueCost AS 'True Cost'
, '0' AS 'Cost Per M'
FROM ns.tinvoice i
JOIN ns.transaction_lines tl on i.transaction_id = tl.transaction_id
JOIN ns.Customers c on c.customer_id = i.ENTITY_ID
JOIN ns.items it on it.item_id = tl.item_id
LEFT JOIN ns.ITEM_CLASSIFICATION it_class on it_class.list_id = it.ITEM_CLASSIFICATION_ID
LEFT JOIN TrueCosts tc ON tc.tranid = i.tranid AND tc.item_id = tl.item_id
WHERE list_item_name IS NOT NULL
AND i.tranid = '1262INV'
You have a couple of problems.
In your subquery you use the alias c for COSReport. You also use the alias c for customers in your outer query.
The bigger problem is that your subquery isn't correlated to your outer query at all. That's why it's summing up the entire table.
To correlate your subquery, you need to join (in the subquery) to one of the tables in the outer query. Not sure of your tables or data, but at a guess, I'd say you want to use ns.tinvoice i in a WHERE clause in your subquery.

[SQL]Find the location, pallet, case, part numbers, and total unit quantity by part number

Facing error while executing a query for :
Find the location, pallet, case, part numbers, and total unit quantity by part number
[select l.stoloc,
il.lodnum,
ins.subnum,
p.prtnum,
sum(i.untqty)
from invdtl as i
inner
join invsub as ins
on i.subnum = ins.subnum
inner
join invlod as il
on il.lodnum = ins.lodnum
inner
join locmst as l
on l.stoloc = il.stoloc
and l.wh_id = il.wh_id
inner
join prtmst as p
on p.prtnum = i.prtnum
where l.stoloc = '10A10B'
group by p.prtnum] catch(-1403)
This is written in MOCA language if you don't know about MOCA kindly ignore catch and square brackets and please help me out to find the correct query.
Currently I am getting following error while executing the above query:
In your query you are aggregating SUM() of i.untqty other columns will have multiple values either you have to include it in your group by clause or apply aggregate functions on them. Depending on the your requirements.
select l.stoloc,
il.lodnum,
ins.subnum,
p.prtnum,
sum(i.untqty)
from invdtl as i
inner
join invsub as ins
on i.subnum = ins.subnum
inner
join invlod as il
on il.lodnum = ins.lodnum
inner
join locmst as l
on l.stoloc = il.stoloc
and l.wh_id = il.wh_id
inner
join prtmst as p
on p.prtnum = i.prtnum
where l.stoloc = '10A10B'
group by p.prtnum,l.stoloc,il.lodnum,ins.subnum
I am currently applying the Group By but you need to apply your related aggregate functions or Group by clause.
select l.stoloc,il.lodnum,ins.subnum,p.prtnum,sum(i.untqty) from invdtl as i inner join invsub as ins on i.subnum = ins.subnum inner join invlod as il on il.lodnum = ins.lodnum inner join locmst as l on l.stoloc = il.stoloc and l.wh_id = il.wh_id inner join prtmst as p on p.prtnum = i.prtnum where l.stoloc = '10A10B' group by l.stoloc,il.lodnum,ins.subnum,p.prtnum;
this should work I think as you are missing l.stoloc in group by clause.because its thumb rule for sql query to include all select stuffs in group by clause.

Reduce Runtime of T-SQL Query

-- WITH POD was causing the issue, removing this code reduced 2 year pull to 3 mins.
Will post new question to figure out best way to include POD data.
--Edit for clarity, I am a read only user to these tables.
I wrote the below query, but it takes a very long time to execute (20min).
It is currently limited to 1 month, but user wants at least 1 year preferably 2. I assume this would scale time to hours.
Can anyone take a look at let me know if there is a BKM I am not using to improve performance?
Or if there is a better method for a report of this size? At 2 years, it would return ~100K rows from 17 tables.
WITH
POD AS
(
SELECT SHIPMENTS.Delivery
,SHIPMENTS.Shipment_Number
,PROOF_OF_DELIVERY.Shipping_Carrier
,PROOF_OF_DELIVERY.Tracking_Number
,PROOF_OF_DELIVERY.Ship_Method
,PROOF_OF_DELIVERY.POD_Signature
,PROOF_OF_DELIVERY.POD_Date
,PROOF_OF_DELIVERY.POD_Time
FROM
SHIPMENTS
LEFT JOIN PROOF_OF_DELIVERY
ON SHIPMENTS.Shipment_Number = PROOF_OF_DELIVERY.Delivery_Or_Shipment
WHERE Load_Date IN
(
SELECT MAX(Load_Date)
FROM PROOF_OF_DELIVERY
GROUP BY Delivery_Or_Shipment
)
)
SELECT DISTINCT GI.GOODS_ISSUE_DOCUMENT_ID
,GI.SALES_ORDER_ID
,GI.SALES_ORDER_LINE_ID
,GI.SALES_ORDER_TYPE_CODE
,GI.DELIVERY_HEADER_ID
,GI.DELIVERY_ITEM_ID
,FD.FISCAL_MONTH_CODE
,GI.MATERIAL_NUMBER
,GI.SHIPPED_QTY
,SO.ORDERER_NAME
,SO.CREATED_BY
,SO.CONTACT_PERSON
,GI.SOLD_TO_CUSTOMER_ID
,GI.SHIP_TO_CUSTOMER_ID
,GI.ORIGINAL_COMMIT_DATE
,GI.SHIP_FROM_PLANT_ID
,GI.ACTUAL_PGI_DATE
,GI.CUSTOMER_PO_NUMBER
,GI.SHIPPED_PRICE
,(GI.SHIPPED_PRICE * GI.SHIPPED_QTY) AS EXT_SHIPPED_PRICE
,GI.SALES_ORGANIZATION_CODE
,GI.DELIVERY_NOTE_PRIORITY_CODE
,FD.FISCAL_WEEK_CODE
,DV.DIVISION_CODE
,DN.Delivery_Item_Creation_Date
,SOLD.CUSTOMER_SHORT_NAME AS SOLD_TO_CUSTOMER_SHORT_NAME
,SHIP.CUSTOMER_SHORT_NAME AS SHIP_TO_CUSTOMER_SHORT_NAME
,SHIP.Customer_Site_Name
,SHIP.REGION_NAME
,MATD.MATERIAL_DESCRIPTION
,MATD.STANDARD_COST
,(MATD.STANDARD_COST * GI.SHIPPED_QTY) AS EXT_STANDARD_COST
,MATD.GLOBAL_EVENT
,PLT.LEAD_TIME_FOR_ORIGINAL_COMMIT
,OPRM.BASE_PART_CODE
,MATD.PRODUCT_INSP_MEMO
,MATD.MATERIAL_PRICING_GROUP_CODE
,MATD.MATERIAL_STATUS AS MMPP
,PIM.PIM_PBG_GROUPING
,SOL.SHIPPING_CONDITION
,SVO.SERVICE_ORDER_NUM
,SO.CREATION_TIME AS SO_CREATION_TIME
,SOL.CREATED_TIME AS SO_LINE_CREATED_TIME
,SOL.SHIPPING_POINT
,SDT.SALES_DOCUMENT_TYPE_CODE AS SVO_DOCUMENT_TYPE_CODE
,EQU.EQUIPMENT_NUM
,EQU.SERIAL_NUMBER
,EQU.CUSTOMERTOOLID
,POD.Shipment_Number
,POD.Shipping_Carrier
,POD.Tracking_Number
,POD.Ship_Method
,POD.POD_Signature
,POD.POD_Date
,POD.POD_Time
,DATEDIFF(dd,SO.CREATION_TIME,GI.ACTUAL_PGI_DATE) AS Cycle_Time_to_PGI_Days
,DATEDIFF(hh,SO.CREATION_TIME,GI.ACTUAL_PGI_DATE) AS Cycle_Time_to_PGI_Hours
FROM GOODS_ISSUE AS GI
INNER JOIN dbo.Delivery_Notes AS DN
ON GI.DELIVERY_HEADER_ID = DN.DELIVERY_HEADER_CODE AND GI.DELIVERY_ITEM_ID = DN.DELIVERY_ITEM_CODE
INNER JOIN dbo.Customer_View AS SOLD
ON GI.SOLD_TO_CUSTOMER_ID = SOLD.CUSTOMER_CODE
INNER JOIN dbo.Customer_View AS SHIP
ON GI.SOLD_TO_CUSTOMER_ID = SHIP.CUSTOMER_CODE
INNER JOIN dbo.MATERIAL_DETAILS AS MATD
ON GI.MATERIAL_NUMBER = MATD.MATERIAL_NUMBER
INNER JOIN dbo.OPR_MATERIAL_DIM AS OPRM
ON OPRM.MATERIAL_NUMBER = GI.MATERIAL_NUMBER
LEFT JOIN dbo.SM_DATE_DIM AS FD
ON CAST(FD.CALENDAR_DAY AS DATE) = CAST(GI.ACTUAL_PGI_DATE AS DATE)
LEFT JOIN dbo.DIM_PUBLISHED_LEAD_TIME_COMMIT AS PLT
ON PLT.MATERIAL_NUMBER = OPRM.BASE_PART_CODE
LEFT JOIN dbo.PRODUCT_INSP_MEMO_DIM AS PIM
ON PIM.PRODUCT_INSP_MEMO = MATD.PRODUCT_INSP_MEMO
INNER JOIN dbo.SM_SALES_ORDER_LINE_FACT AS SOL
ON SOL.SALES_ORDER_CODE = GI.SALES_ORDER_ID AND SOL.SALES_ORDER_LINE_CODE = GI.SALES_ORDER_LINE_ID
INNER JOIN dbo.SM_SALES_ORDER_FACT AS SO
ON SO.SALES_ORDER_CODE = GI.SALES_ORDER_ID
INNER JOIN dbo.SM_DIVISION_DIM AS DV
ON SO.DIVISION_SID = DV.DIVISION_SID
LEFT JOIN dbo.SERVICE_ORDER_FACT AS SVO
ON SVO.SERVICE_ORDER_NUM = SO.SERVICE_ORDER_NUMBER
LEFT JOIN dbo.SM_SALES_DOCUMENT_TYPE_DIM AS SDT
ON SDT.SALES_DOCUMENT_TYPE_SID = SVO.SALES_DOCUMENT_TYPE_SID
LEFT JOIN dbo.SM_EQUIPMENT_DIM AS EQU
ON EQU.EQUIPMENT_SID = SVO.EQUIPMENT_SID
LEFT JOIN POD
ON POD.Delivery = GI.DELIVERY_HEADER_ID
WHERE GI.ACTUAL_PGI_DATE > GETDATE()-32
AND SOLD_TO_CUSTOMER_ID IN (0010000252,0010000898,0010001121,0010001409,0010001842,0010001852,0010001879,0010001977,0010001978,0010002021,0010002202,0010002227,0010002982,0010003118,0010003176,0010003294,0010005492,0010006904,0010007048,0010007080,0010010381,0010010572,0010010905,0010011999,0010012014,0010012048,0010012571,0010013124,0010013711,0010013713,0010013824,0010014180,0010014188,0010014333,0010015059,0010015313,0010015414,0010015541,0010015544,0010015550)
A CTE is just syntax
I suspect that CTE is evaluated many times
Materialze the CTE to #temp with indexe(s) so it is run once
This cast will hurt it
Make those columns true dates and index them
ON CAST(FD.CALENDAR_DAY AS DATE) = CAST(GI.ACTUAL_PGI_DATE AS DATE)
That where negates the left so you can just do a join
Also that MAX(Load_Date) could match on another shipment
SELECT SHIPMENTS.Delivery
,SHIPMENTS.Shipment_Number
,PROOF_OF_DELIVERY.Shipping_Carrier
,PROOF_OF_DELIVERY.Tracking_Number
,PROOF_OF_DELIVERY.Ship_Method
,PROOF_OF_DELIVERY.POD_Signature
,PROOF_OF_DELIVERY.POD_Date
,PROOF_OF_DELIVERY.POD_Time
FROM SHIPMENTS
JOIN PROOF_OF_DELIVERY
ON SHIPMENTS.Shipment_Number = PROOF_OF_DELIVERY.Delivery_Or_Shipment
WHERE PROOF_OF_DELIVERY.Load_Date IN
(
SELECT MAX(Load_Date)
FROM PROOF_OF_DELIVERY
GROUP BY Delivery_Or_Shipment
)
Pull this up into the join
INNER JOIN dbo.Customer_View AS SOLD
ON GI.SOLD_TO_CUSTOMER_ID = SOLD.CUSTOMER_CODE
AND GI.SOLD_TO_CUSTOMER_ID IN (0010000252,0010000898,0010001121,0010001409,0010001842,0010001852,0010001879,0010001977,0010001978,0010002021,0010002202,0010002227,0010002982,0010003118,0010003176,0010003294,0010005492,0010006904,0010007048,0010007080,0010010381,0010010572,0010010905,0010011999,0010012014,0010012048,0010012571,0010013124,0010013711,0010013713,0010013824,0010014180,0010014188,0010014333,0010015059,0010015313,0010015414,0010015541,0010015544,0010015550)