How to Join tables in SQL with SUM Function and Group? - sql

I'm creating a report to display project wise cost and revenue. I need to group sum of A/P invoices and Sum of A/R invoices according to the project.
Same project could have many A/P invoices and A/R invoices which can include same Items.If there have 5 A/P invoices with same Items Like A,B,C I need to take Item wise sum of the amount also(Line wise Total sum)
All projects names and code(PrjCode-Primary Key) based in a separate table of OPRJ
Other Tables Structure as follows
A/P Invoice - OPCH(Main details) Link with PCH1(Item Details)
On Primary Key - DocEntry
PCH1 Lines Link with OPRJ on PCH1.Project = OPRJ.PrjCode
A/R Invoice - OINV(Main details) Link with INV1(Item Details)
On Primary Key - DocEntry
INV1 Lines Link with OPRJ on INV1.Project = OPRJ.PrjCode
I tries this query but couldn't get sum
select a.PrjCode,a.PrjName,b.ItemCode,sum(b.LineTotal),Sum(c.DocTotal),sum(e.DocTotal)
from OPRJ a
Left Join PCH1 b on b.Project = a.PrjCode
Left Join OPCH c on c.DocEntry = b.DocEntry
Left Join INV1 d on d.Project = a.PrjCode and b.Project = d.Project
Left Join OINV e on e.DocEntry = d.DocEntry
Group by
a.PrjCode,a.PrjName,c.DocTotal,e.DocTotal,b.ItemCode,b.LineTotal
Output what I expect is as follows
Sql Output without sum

you should remove the values you're summing up from the group by:
select a.PrjCode,a.PrjName,b.ItemCode,sum(b.LineTotal),Sum(c.DocTotal),sum(e.DocTotal)
from OPRJ a
Left Join PCH1 b on b.Project = a.PrjCode
Left Join OPCH c on c.DocEntry = b.DocEntry
Left Join INV1 d on d.Project = a.PrjCode and b.Project = d.Project
Left Join OINV e on e.DocEntry = d.DocEntry
Group by
a.PrjCode,a.PrjName,b.ItemCode

Related

SQL joins not giving me correct totals

I'm trying to get a report for figures from multiple transaction tables. Each table has a foreign key that is a lookup for the day it was taken on, and what location it was taken at called Site_Lookup_Id. It's giving me figures that are much larger than they should be.
#Site_Lookup_Ids dbo.Id_List READONLY
SELECT SL.Site_Lookup_Id, D.[Start],SUM(I.Amount) AS Income,
SUM(P.Amount) AS Payouts,SUM(DP.Amount) AS Deposit
FROM CashUp_Site_Lookup SL
INNER JOIN #Site_Lookup_Ids IDs ON Ids.Id = SL.Site_Lookup_Id
INNER JOIN CashUp_Day D ON SL.Day_Id = D.Day_Id
LEFT JOIN CashUp_Deposit DP ON DP.Redeemed_Site_Lookup_Id = SL.Site_Lookup_Id
AND DP.No_Show != 1
LEFT JOIN CashUp_Income I ON I.Site_Lookup_Id = SL.Site_Lookup_Id
LEFT JOIN CashUp_Payout P ON P.Site_Lookup_Id = SL.Site_Lookup_Id
GROUP BY SL.Site_Lookup_Id, D.[Start]
Not all sums will have a value as some days no transactions for a given table will be taken on the day - In this the value should be zero.
The issue is that running this gives me crazy high values - £7500 for income against one day, when if I do a simple check it's £40 for that day like so.
SELECT SUM(Amount) FROM Cashup_Income WHERE Site_Lookup_Id IN (values...)
Maybe something like...
It really depends on the relationships and when you want the values to be summed.
SELECT SL.Site_Lookup_Id
, D.[Start]
, SUM(I.Amount) over (partition by Key of I table) AS Income
, SUM(P.Amount) over (partition by Key of P table) AS Payouts
, SUM(DP.Amount) over (partition by Key of DP Table) AS Deposit
FROM CashUp_Site_Lookup SL
INNER JOIN #Site_Lookup_Ids IDs ON Ids.Id = SL.Site_Lookup_Id
INNER JOIN CashUp_Day D ON SL.Day_Id = D.Day_Id
LEFT JOIN CashUp_Deposit DP ON DP.Redeemed_Site_Lookup_Id = SL.Site_Lookup_Id
AND DP.No_Show != 1
LEFT JOIN CashUp_Income I ON I.Site_Lookup_Id = SL.Site_Lookup_Id
LEFT JOIN CashUp_Payout P ON P.Site_Lookup_Id = SL.Site_Lookup_Id
GROUP BY SL.Site_Lookup_Id, D.[Start]
The problem stems from the fact that your tables are 1:M Causing values to repeat. These repeated values are then getting added to your sum. The joins cause this issue. So I think you can sum using a partition to eliminate the duplicates or:
Use derived tables or a CTE and sum the values BEFORE you join.
Using CTE's (Common Table Expressions)
WITH DP AS (SELECT sum(Amount) As Deposit
, Redeemed_Site_Lookup_ID
FROM CashUp_Deposit
WHERE No_Show !=1
GROUP BY Redeemed_Site_Lookup_ID),
I AS (SELECT sum(Amount) as Income
, Site_Lookup_Id
FROM CashUp_Income
GROUP BY Site_Lookup_Id),
P AS (SELECT sum(Amount) as Payouts
, Site_Lookup_Id
FROM CashUp_Payout
GROUP BY Site_Lookup_Id)
SELECT SL.Site_Lookup_Id
, D.[Start]
, Income
, Payouts
, Deposit
FROM CashUp_Site_Lookup SL
INNER JOIN #Site_Lookup_Ids IDs
ON Ids.Id = SL.Site_Lookup_Id
INNER JOIN CashUp_Day D
ON SL.Day_Id = D.Day_Id
LEFT JOIN DP
ON DP.Redeemed_Site_Lookup_Id = SL.Site_Lookup_Id
LEFT JOIN I
ON I.Site_Lookup_Id = SL.Site_Lookup_Id
LEFT JOIN P
ON P.Site_Lookup_Id = SL.Site_Lookup_Id
Presumably, you are generating a Cartesian product with your joins. Because you have no filtering, do the aggregation before the joins:
LEFT JOIN
(SELECT DP.Redeemed_Site_Lookup_Id, SUM(DP.Amount) AS Deposit
FROM CashUp_Deposit DP
WHERE DP.No_Show != 1
GROUP BY DP.Redeemed_Site_Lookup_Id
) DP
ON DP.Redeemed_Site_Lookup_Id = SL.Site_Lookup_Id LEFT JOIN
(SELECT I.Site_Lookup_Id, SUM(I.Amount) AS Income
FROM CashUp_Income I
GROUP BY I.Site_Lookup_Id
) I
ON I.Site_Lookup_Id = SL.Site_Lookup_Id LEFT JOIN
(SELECT P.Site_Lookup_Id, SUM(P.Amount) AS Payout
FROM CashUp_Payout P
GROUP BY I.Site_Lookup_Id
) P
ON P.Site_Lookup_Id = SL.Site_Lookup_Id
Then adjust the rest of your query to remove the GROUP BY and SUM()s.

Oracle SQL How to Count Column Value Occurences and Group BY during joins

I'm working on another SQL query, trying to group a collection of records while doing a count and joining tables. See below for goal, current query, and attached scripts for building and populating tables.
Show all customers who have checked more books than DVDs. Display
customer name, total book checkouts and total DVD checkouts. Sort
results by customer first name and last name.
SELECT C.CUSTOMER_FIRSTNAME, C.CUSTOMER_LASTNAME, COUNT(T.TRANSACTION_ID)
FROM customer C
INNER JOIN library_card LC ON C.CUSTOMER_ID = LC.CUSTOMER_ID
INNER JOIN transaction T ON LC.LIBRARY_CARD_ID = T.LIBRARY_CARD_ID
INNER JOIN physical_item P ON T.PHYSICAL_ITEM_ID = P.PHYSICAL_ITEM_ID
INNER JOIN catalog_item CT ON P.CATALOG_ITEM_ID = CT.CATALOG_ITEM_ID
GROUP BY C.CUSTOMER_FIRSTNAME, C.CUSTOMER_LASTNAME
ORDER BY C.CUSTOMER_FIRSTNAME, C.CUSTOMER_LASTNAME;
Run first: https://drive.google.com/open?id=1PYAZV4KIfZtxP4eQn35zsczySsxDM7ls
Run second: https://drive.google.com/open?id=1pAzWmJqvD3o3n6YJqVUM6TtxDafKGd3f
EDIT
With some help from Mr. Barbaros I've come up with the below query, which is closer. However, this query isn't returning any results for DVDs, which leads me to believe it's a join issue.
SELECT C.CUSTOMER_FIRSTNAME, C.CUSTOMER_LASTNAME, COUNT(CT1.TYPE) AS BOOK_COUNT, COUNT(CT2.TYPE) AS DVD_COUNT
FROM customer C
INNER JOIN library_card LC ON C.CUSTOMER_ID = LC.CUSTOMER_ID
INNER JOIN transaction T ON LC.LIBRARY_CARD_ID = T.LIBRARY_CARD_ID
INNER JOIN physical_item P ON T.PHYSICAL_ITEM_ID = P.PHYSICAL_ITEM_ID
INNER JOIN catalog_item CT1 ON P.CATALOG_ITEM_ID = CT1.CATALOG_ITEM_ID AND CT1.TYPE = 'BOOK'
LEFT OUTER JOIN catalog_item CT2 ON P.CATALOG_ITEM_ID = CT2.CATALOG_ITEM_ID AND CT2.TYPE = 'DVD'
GROUP BY C.CUSTOMER_FIRSTNAME, C.CUSTOMER_LASTNAME, CT1.TYPE, CT2.TYPE
ORDER BY C.CUSTOMER_FIRSTNAME, C.CUSTOMER_LASTNAME;
Use "conditional aggregates" (use a case expression inside the aggregate function)
SELECT
C.CUSTOMER_FIRSTNAME
, C.CUSTOMER_LASTNAME
, COUNT( CASE WHEN CT.TYPE = 'BOOK' THEN T.TRANSACTION_ID END ) books
, COUNT( CASE WHEN CT.TYPE = 'DVD' THEN T.TRANSACTION_ID END ) dvds
FROM customer C
INNER JOIN library_card LC ON C.CUSTOMER_ID = LC.CUSTOMER_ID
INNER JOIN transaction T ON LC.LIBRARY_CARD_ID = T.LIBRARY_CARD_ID
INNER JOIN physical_item P ON T.PHYSICAL_ITEM_ID = P.PHYSICAL_ITEM_ID
INNER JOIN catalog_item CT ON P.CATALOG_ITEM_ID = CT.CATALOG_ITEM_ID
GROUP BY
C.CUSTOMER_FIRSTNAME
, C.CUSTOMER_LASTNAME
HAVING
COUNT( CASE WHEN CT.TYPE = 'BOOK' THEN T.TRANSACTION_ID END )
> COUNT( CASE WHEN CT.TYPE = 'DVD' THEN T.TRANSACTION_ID END )
ORDER BY
C.CUSTOMER_FIRSTNAME
, C.CUSTOMER_LASTNAME
;
You can use catalog_item table twice( think of as seperate tables for books and dvds ), and compare by HAVING clause as :
SELECT C.CUSTOMER_FIRSTNAME, C.CUSTOMER_LASTNAME,
COUNT(CT1.CATALOG_ITEM_ID) as "Book Checkout",
COUNT(CT2.CATALOG_ITEM_ID) as "DVD Checkout"
FROM customer C
INNER JOIN library_card LC ON C.CUSTOMER_ID = LC.CUSTOMER_ID
INNER JOIN transaction T ON LC.LIBRARY_CARD_ID = T.LIBRARY_CARD_ID
INNER JOIN physical_item P ON T.PHYSICAL_ITEM_ID = P.PHYSICAL_ITEM_ID
LEFT JOIN catalog_item CT1 ON P.CATALOG_ITEM_ID = CT1.CATALOG_ITEM_ID AND CT1.TYPE = 'BOOK'
LEFT JOIN catalog_item CT2 ON P.CATALOG_ITEM_ID = CT2.CATALOG_ITEM_ID AND CT1.TYPE = 'DVD'
GROUP BY C.CUSTOMER_FIRSTNAME, C.CUSTOMER_LASTNAME
HAVING COUNT(CT1.CATALOG_ITEM_ID) > COUNT(CT2.CATALOG_ITEM_ID)
ORDER BY C.CUSTOMER_FIRSTNAME, C.CUSTOMER_LASTNAME;
CUSTOMER_FIRSTNAME CUSTOMER_LASTNAME Book Checkout DVD Checkout
------------------ ----------------- ------------- -------------
Deena Pilgrim 3 1
Emile Cross 5 2
Please try to remove ,CT1.TYPE, CT2.TYPE on your group by clause.

What is the link in SAP Business One between Sales Orders, Deliveries, and Invoices

I'm trying to join ORDR, ODLN and OINV in a SAP Business One query, but I can't seem to find a field that they share in common.
There must be some record somewhere that links one to another.
Are they linked via a separate table? Or am I missing something obvious?
I am using SAP HANA as my DB, so queries in HANA are preferred rather than MSSQL.
First, credit to Eralper for their answer, as the link contained in it helped me find the solution I was looking for. However, their solution does not include an explanation and does not quite give the result that is being looked for.
The main information for a Sales Order in SAP is stored in two tables, ORDR and RDR1. ORDR has one line for each Sales Order, while RDR1 has one line for each product row on the Sales Order.
Delivery Notes and Invoices (and basically any document in SAP) follow this pattern.
Why is this important to this question? Because the column that contains the data to link Sales Orders, Delivery Notes and Invoices is in RDR1 (or the similar variant). It's name is TrgetEntry.
As there is a row for each product on a Sales Order, we can't simply do a join, as any Sales Order that has more than one product will appear multiple times in the result. The following query uses grouping to show a table that has a line for each Sales Order, and has the needed information to link it to Delivery Notes.
SELECT T0."DocEntry" AS "SO DE", T0."DocNum" AS "Sales Order Number", T1."TrgetEntry" AS "SO TE", COUNT(T0."DocNum") AS "Rows"
FROM ORDR T0
LEFT JOIN RDR1 T1 ON T0."DocEntry" = T1."DocEntry"
GROUP BY T0."DocEntry", T0."DocNum", T1."TrgetEntry"
By just changing the table names, similar queries can be created for Delivery Notes and Invoices.
Then you can use the TrgetEntry and DocEntry to link the various results.
The final code I use to show Sales Orders, their related Deliveries and Invoices is the following:
SELECT S0."SalesOrderNumber", S1."DeliveryNumber", S2."DocNum" AS "InvoiceNumber", S0."Rows", S2."DocTotal"
FROM (SELECT T0."DocEntry" AS "SO_DE", T0."DocNum" AS "SalesOrderNumber", T1."TrgetEntry" AS "SO_TE", COUNT(T0."DocNum") AS "Rows"
FROM ORDR T0
LEFT JOIN RDR1 T1 ON T0."DocEntry" = T1."DocEntry"
GROUP BY T0."DocEntry", T0."DocNum", T1."TrgetEntry") S0
LEFT JOIN (SELECT T0."DocEntry" AS "DN_DE", T0."DocNum" AS "DeliveryNumber", T1."TrgetEntry" AS "DN_TE"
FROM ODLN T0
LEFT JOIN DLN1 T1 ON T0."DocEntry" = T1."DocEntry"
GROUP BY T0."DocEntry", T0."DocNum", T1."TrgetEntry") S1 ON S0."SO_TE" = S1."DN_DE"
LEFT JOIN OINV S2 ON S1."DN_TE" = S2."DocEntry"
Please check https://archive.sap.com/discussions/thread/1440163
There following relationship is given
SELECT Distinct(T0.DocNum ),T0.DocDate, T0.CardCode, T0.CardName, T1.ItemCode, T1.Quantity, T1.Price,T1.TotalSumSy, T0.DocTotal
FROM ORDR T0
INNER JOIN RDR1 T1 ON T0.DocEntry = T1.DocEntry
INNER JOIN ODLN T2 ON T2.DocEntry = T1.TrgetEntry
INNER JOIN DLN1 T3 on T3.DocEntry = T2.Docentry
INNER JOIN OINV T4 ON T4.DocEntry = T3.TrgetEntry
INNER JOIN INV1 T5 ON T5.DocEntry = T4.DocEntry
LEFT JOIN ORDN T6 ON T6.DocEntry = T5.TrgetEntry
LEFT JOIN RDN1 T7 ON T7.DocEntry = T6.DocEntry
So following relation is also true
SELECT *
FROM ODLN T2
INNER JOIN DLN1 T3 on T3.DocEntry = T2.Docentry
INNER JOIN OINV T4 ON T4.DocEntry = T3.TrgetEntry

SAP B1 Link AP invoice to PO user

I am trying to generate a query that will return header information for Payment-Hold AP Invoices but also show the username that entered the originating Purchase Order.
Some of my invoices do not have a relationship history so I wouldn't expect any user details to be returned but still need the invoice header information. Others have multiple GRPO entries and are therefore returning 2-3 lines where I only need one. Below is my query.
thanks in advance.
select distinct T0."DocNum"
,T0."DocDate"
,T0."DocType"
,T0."CardCode"
,T0."CardName"
,T0."DocStatus"
,T0."GroupNum"
,T0."DocDueDate"
,T0."DocCur"
,T0."DocTotal"
,T0."DocTotalFC"
,T0."PayBlock"
,T0."PaymentRef"
--,T1."DocEntry"
--,T2."DocEntry"
--,T4."DocNum"
--,T3."ItemCode"
--,IFNULL(T4."UserSign", 0) AS "User_Code"
-- ,T4."UserSign"
,T5."U_NAME" as "Purc_User"
,IFNULL(T5."U_NAME",'') as "PURC_USER"
,'HT' as Entity
,T0."Comments"
FROM OPCH T0
left JOIN PCH1 T1 ON T1."DocEntry" = T0."DocEntry"
left JOIN PDN1 T2 on T1."BaseEntry"= T2."DocEntry" and T1."BaseLine"= T2."LineNum"
left JOIN POR1 T3 on T2."BaseEntry" = T3."DocEntry" and T2."BaseLine" = T3."LineNum"
left JOIN OPOR T4 on T4. "DocEntry" = T3."DocEntry"
left JOIN OUSR T5 on T5."USERID" = T4."UserSign"
where T0."DocStatus" = 'O'
and T0."PayBlock" = 'Y'
order by T0."DocNum"

List all donations made, by both individual alumni and business donors. Name, ID of the donor, date and amount of the donation must be displayed

Need help in this problem. I have the following tables but i cannot seem to get any data out based on the description and query as below.
Corporate(CorporateID(PK), CorporateName, CorporateAddress)
Donation(DonationID(PK), TypeOfDonations)
Alumnus(AlumnusID(PK), CityPK(FK), AlumnusName, EmailAddress, WorkPhoneNumber, HomePhoneNumber, Address
Donation_Made(CorporateDonationID(PK), DonationID(FK), CorporateID(FK), AlumnusID(FK), DonationAmount, DateOfDonation
SELECT Z.DONATIONID, A.ALUMNUSNAME, C.CORPORATENAME, Z.DATEOFDONATION, Z.DONATIONAMOUNT
FROM ALUMNUS A,
(SELECT * FROM DONATION D LEFT JOIN DONATION_MADE DM
ON D.DONATIONID = DM.DONATIONID)Z LEFT JOIN CORPORATE C
ON C.CORPORATEID = DM.CORPORATEID AND A.ALUMNUSID=DM.ALUMNUSID AND Z.TYPEOFDONATIONS= 'MONETARY';
You should start with the fact table, i.e. DONATION_MADE, and then (outer) join the related tables:
SELECT DM.DONATIONID,
A.ALUMNUSNAME,
C.CORPORATENAME,
DM.DATEOFDONATION,
DM.DONATIONAMOUNT
FROM DONATION_MADE DM
INNER JOIN DONATION D
ON D.DONATIONID = DM.DONATIONID
LEFT JOIN ALUMNUS A,
ON A.ALUMNUSID = DM.ALUMNUSID
LEFT JOIN CORPORATE C
ON C.CORPORATEID = DM.CORPORATEID
WHERE D.TYPEOFDONATIONS = 'MONETARY';
You need to use outer join to get data from both Corporate and Alumnus tables.
select a.AlumnusID, a.AlumnusName, c.CorporateID, c.CorporateName, dm.DateOfDonation, dm.DonationAmount
from Donation_Made dm
left outer join on Alumnus a on dm.AlumnusID = a.AlumnusID
left outer join on Corporate c on dm.CorporateID = c.CorporateID
inner join on Donation d on dm.DonationID = d.DonationID
where d.TypeOfDonations='MONETARY';