I'm trying to create a SQL item history stockcard using an MS Access database in VB.NET. I have the SQL below, but it's not right yet:
SELECT CodeProduct, Sum(IIf(Cat="IN", Qty, 0)) AS [IN], Sum(IIf(Cat="Out", Qty, 0)) AS OUT, Sum(Qty * IIf(Cat="OUT", -1, 1)) AS BALANCE
FROM (
SELECT [No], Invono, CodeProduct,Qty, "IN" AS Cat FROM PurchaseDetail
UNION
SELECT [No], Invono, CodeProduct, Qty, "OUT" FROM SalesDetail
) AS DetailUNION
WHERE CodeProduct = 'A'
GROUP BY CodeProduct;
Results from the above SQL code
CodeProduct | IN | OUT | BALANCE |
---|---|---|---|
A | 75 | 65 | 10 |
Sample Data :
Table Purchase
Invono | Invodate | Transaction | Remark | NameSC |
---|---|---|---|---|
1000 | 18-Oct-23 | Purchase | REPEAT AGAIN | TEST1 |
1001 | 19-Oct-23 | Purchase | TEST2 |
Table PurchaseDetail
Invono | No | Codeproduct | Qty | Info |
---|---|---|---|---|
1000 | 1 | A | 50 | WHITE |
1000 | 2 | B | 35 | |
1001 | 1 | C | 17 | |
1001 | 2 | A | 25 | BROWN |
Table Sales
Invono | Invodate | Transaction | Remark | NameSC |
---|---|---|---|---|
1000 | 18-Oct-23 | Sales | TEST10 | |
1001 | 19-Oct-23 | Sales | TEST20 | |
1002 | 20-Oct-23 | Sales | TEST30 |
Table SalesDetail
Invono | No | Codeproduct | Qty | Info |
---|---|---|---|---|
1000 | 1 | A | 25 | WHITE |
1001 | 1 | C | 35 | |
1001 | 2 | A | 15 | WHITE |
1002 | 1 | A | 25 | BROWN |
Desired Result
Invono | Invodate | Transaction | No | Codeproduct | Info | Remark | NameSC | IN | OUT | BALANCE |
---|---|---|---|---|---|---|---|---|---|---|
1000 | 18-Oct-23 | Purchase | 1 | A | WHITE | REPEAT AGAIN | TEST1 | 50 | 50 | |
1000 | 18-Oct-23 | Sales | 1 | A | WHITE | TEST10 | 25 | 25 | ||
1001 | 19-Oct-23 | Purchase | 2 | A | BROWN | TEST2 | 25 | 50 | ||
1001 | 19-Oct-23 | Sales | 2 | A | WHITE | TEST20 | 15 | 35 | ||
1002 | 20-Oct-23 | Sales | 1 | A | BROWN | TEST30 | 25 | 10 | ||
... |
Include Sales and Purchase tables in the UNION lines with INNER JOIN:
SELECT InvoDate, Transaction, InvoNo, CodeProduct, NameSC,
Sum(IIf(Transaction="Purchase", Qty, 0)) AS [IN],
Sum(IIf(Transaction="Sales", Qty, 0)) AS OUT,
Sum(Qty * IIf(Transaction="Sales", -1, 1)) AS BALANCE
FROM (
SELECT [No], InvoDate, Purchase.Invono, CodeProduct, Qty, NameSC, Transaction
FROM PurchaseDetail INNER JOIN Purchase
ON Purchase.Invono = PurchaseDetail.Invono
UNION
SELECT [No], InvoDate, Sales.Invono, CodeProduct, Qty, NameSC, Transaction
FROM SalesDetail INNER JOIN Sales
ON Sales.Invono = SalesDetail.Invono
) AS DetailUNION
WHERE CodeProduct = 'A'
GROUP BY InvoDate, Transaction, InvoNo, CodeProduct, NameSC;
InvoDate | Transaction | InvoNo | CodeProduct | NameSC | IN | OUT | BALANCE |
---|---|---|---|---|---|---|---|
10/18/2023 | Purchase | 1000 | A | TEST1 | 50 | 0 | 50 |
10/18/2023 | Sales | 1000 | A | TEST10 | 0 | 25 | -25 |
10/19/2023 | Purchase | 1001 | A | TEST2 | 25 | 0 | 25 |
10/19/2023 | Sales | 1001 | A | TEST20 | 0 | 15 | -15 |
10/20/2023 | Sales | 1002 | A | TEST30 | 0 | 25 | -25 |
The BALANCE shown in your output sample is a running total. Running total in query is a common topic. It is difficult to accomplish in Access query. One approach uses DSum(), another requires correlated subquery. Each can perform slowly with large dataset. It is simple to produce a running total in Access report using Sorting & Grouping and textbox RunningSum property.
Your running balance is by CodeProduct. Following version shows running total. Unfortunately, value on each row will not agree with IN OUT BALANCE values because running total subquery is not calculating records in the same order. Also, when there are multiple records for the same date, they show the same value because there is no way to differentiate them in the subquery for sequential calc. This is usually accomplished by querying a table directly and including a unique ID field such as autonumber field in the WHERE clause. Since your data set is a complex query, not just a table, this is not available. However, final balance on last row is correct. Excluded WHERE clause so results will show for all CodeProduct values. Add filtering as you see fit.
First, the DetailUNION SQL must be a saved query object that is then called to perform the aggregation and running total. Embedding its SQL as a subquery fails.
New Info: Including NameSC criteria in RunningBal subquery does allow differentiation of multiple same-date records and calculations will then agree. However, I doubt actual data in table can be relied on.
SELECT InvoDate, Transaction, InvoNo, CodeProduct, NameSC,
Sum(IIf(Transaction="Purchase",Qty,0)) AS [IN],
Sum(IIf(Transaction="Sales",Qty,0)) AS OUT,
Sum(Qty*IIf(Transaction="Sales",-1,1)) AS BALANCE,
(SELECT Sum(Qty * IIf(Transaction="Sales",-1,1))
FROM DetailUNION as Dupe
WHERE Dupe.CodeProduct=DetailUNION.CodeProduct
AND Dupe.InvoDate<=DetailUNION.InvoDate
AND Dupe.NameSC<=DetailUNION.NameSC) AS RunningBal
FROM DetailUNION
GROUP BY InvoDate, Transaction, InvoNo, CodeProduct, NameSC
ORDER BY CodeProduct, InvoDate, NameSC;
InvoDate | Transaction | InvoNo | CodeProduct | NameSC | IN | OUT | BALANCE | RunningBal |
---|---|---|---|---|---|---|---|---|
10/18/2023 | Purchase | 1000 | A | TEST1 | 50 | 0 | 50 | 50 |
10/18/2023 | Sales | 1000 | A | TEST10 | 0 | 25 | -25 | 25 |
10/19/2023 | Purchase | 1001 | A | TEST2 | 25 | 0 | 25 | 50 |
10/19/2023 | Sales | 1001 | A | TEST20 | 0 | 15 | -15 | 35 |
10/20/2023 | Sales | 1002 | A | TEST30 | 0 | 25 | -25 | 10 |
10/18/2023 | Purchase | 1000 | B | TEST1 | 35 | 0 | 35 | 35 |
10/19/2023 | Purchase | 1001 | C | TEST2 | 17 | 0 | 17 | 17 |
10/19/2023 | Sales | 1001 | C | TEST20 | 0 | 35 | -35 | -18 |