Wrong Opening Balance for some rows - sql

I want to calculate Opening and Closing Balance of a business application. But for some rows wrong Opening Balance is producing. I have following Data Tables:
SupplierPayments
DateOfPayment Bill
2018-06-01 4000
2018-06-01 9000
2018-06-19 2000
2018-06-19 6000
2019-03-28 3000
2019-03-29 5000
Expensis
DateOfExpense Expense
2018-08-14 2,000
2019-02-26 8,000
2019-03-28 2000
2019-03-29 2000
Income
DateSold Income
2018-09-27 24,000
2018-10-17 8,000
2019-01-01 13,000
2019-03-28 10,000
SQL Server 2012 Query
with Income( DateSold, Income ) as (
select DateSold,isnull(sum(TotalBill),0)
from SalesInvoice group by DateSold
), SupplierPayments( DateOfPayment,Bill ) as(
select DateOfPayment,isnull(sum(BillPaidAmount),0)
from SupplyInvoicePaymentHistory group by DateOfPayment
), Expensis( DateOfExpense, Expense ) as(
select Date ,isnull(sum(Amount),0)
from GeneralExpense group by Date
), t as (
select i.DateSold
,e.DateOfExpense
,sp.DateOfPayment
,i.income
, e.Expense
,sp.Bill
, sum(isnull(i.income,0)-(isnull(e.Expense,0)+isnull(sp.Bill,0))) over (order by i.DateSold,e.DateOfExpense,sp.DateOfPayment) as closing_balance
from income i
full outer join expensis e on e.DateOfExpense = i.DateSold
full outer join SupplierPayments sp on sp.DateOfPayment=e.DateOfExpense
)
select m.EventDate, m.DateSold
,m.DateOfExpense
,m.DateOfPayment
,isnull(m.opening_balance,0) as Opening_Balance
,isnull(m.Income,0) as Income
,isnull(m.Expense,0) as Expensis
,isnull(m.Bill,0) as SupplierPayments
,isnull(m.closing_balance,0) as Closing_Balance
from (
select coalesce(coalesce(DateOfPayment, DateOfExpense), DateSold) EventDate, DateSold
,DateOfExpense
,DateOfPayment
,lag(closing_balance,1,0) over (order by DateSold, DateOfExpense,DateOfPayment) as opening_balance
,Income
,Expense
,closing_balance
,Bill
from t
) as m order by m.EventDate ASC
Output
EventDate DateSold ExpenseDate PaymentDate Opening Income Expense Bill Closing
2018-06-01 NULL NULL 2018-06-01 0 0 0 13000 -13000
2018-06-19 NULL NULL 2018-06-19 -13000 0 0 8000 -21000
2018-08-14 NULL 2018-08-14 NULL -21000 0 2000 0 -23000
2018-09-27 2018-09-27 NULL NULL -30000 24000 0 0 -6000
2019-01-01 2019-01-01 NULL NULL -6000 13000 0 0 7000
2019-03-28 2019-03-28 2019-03-28 2019-03-28 7000 10000 2000 3000 12000
2019-03-29 NULL 2019-03-29 2019-03-29 -23000 0 2000 5000 -30000
Formula to calculate closing balance is as:
Closing = Opening + Income - Expense - Bill
As we can notice that opening balance for date 2018-09-27 is -30,000 which is wrong. It should be -23,000. similarly opening balance for date 2019-03-29 is also wrong.
Required Result
EventDate DateSold ExpenseDate PaymentDate Opening Income Expense Bill Closing
2018-06-01 NULL NULL 2018-06-01 0 0 0 13000 -13000
2018-06-19 NULL NULL 2018-06-19 -13000 0 0 8000 -21000
2018-08-14 NULL 2018-08-14 NULL -21000 0 2000 0 -23000
2018-09-27 2018-09-27 NULL NULL -23000 24000 0 0 1000
2019-01-01 2019-01-01 NULL NULL 1000 13000 0 0 14000
2019-03-28 2019-03-28 2019-03-28 2019-03-28 14000 10000 2000 3000 19000
2019-03-29 NULL 2019-03-29 2019-03-29 19000 0 2000 5000 12000
It is possible that in any day there is No item sold but there is expense or bill paid to supplier and vice versa.
Also it is possible that any tables has two entries on the same date.

Your issue lies within the order by columns for both SUM and LAG. If you selected from t CTE alone, you'd see that you're not ordering by whatever is in any of the three dates (what's available), you're ordering by them in the order you specified. So you'll get NULLs for the first column first, NULLs for the second column first, etc. What you need to do is introduce the EventDate sooner, in t, and order everything by it.
;with xIncome( DateSold, Income ) as (
select DateSold,isnull(sum(income),0)
from income group by DateSold
), xSupplierPayments( DateOfPayment,Bill ) as(
select DateOfPayment,isnull(sum(bill),0)
from supplierpayments group by DateOfPayment
), xExpensis( DateOfExpense, Expense ) as(
select DateOfExpense Date ,isnull(sum(expense),0)
from expensis group by Dateofexpense
), t as (
select i.DateSold
,e.DateOfExpense
,sp.DateOfPayment
,consolidated.date consolidatedDate
,i.income
, e.Expense
,sp.Bill
, sum(isnull(i.income,0)-(isnull(e.Expense,0)+isnull(sp.Bill,0))) over (order by consolidated.date) as closing_balance
from xincome i
full outer join xexpensis e on e.DateOfExpense = i.DateSold
full outer join xSupplierPayments sp on sp.DateOfPayment=e.DateOfExpense
cross apply (select coalesce(i.DateSold,e.DateOfExpense,sp.DateOfPayment) as date) consolidated
)
select consolidatedDate, m.DateSold
,m.DateOfExpense
,m.DateOfPayment
,isnull(m.opening_balance,0) as Opening_Balance
,isnull(m.Income,0) as Income
,isnull(m.Expense,0) as Expensis
,isnull(m.Bill,0) as SupplierPayments
,isnull(m.closing_balance,0) as Closing_Balance
from (
select consolidatedDate
,DateSold
,DateOfExpense
,DateOfPayment
,lag(closing_balance,1,0) over (order by consolidatedDate) as opening_balance
,Income
,Expense
,closing_balance
,Bill
from t
) as m order by m.consolidatedDate ASC
Notice the CROSS APPLY in t CTE, where I COALESCE all the dates into a single cosnolidated date. I had to rename the first CTEs to match your sample data table names, but you get the gist of it.

Related

Calculating a difference for an item, between most recent dates

I'm looking to calculate the difference between weights of my loaded trucks everyday. Basically, I want to know the incremental weight amounts carried each day. In theory, the trucks will be running on a daily basis, but I'm using a simplified data set on my mock database.
This is the code I've come up with:
--create table dbo.truck
--(TruckID int, Weight float, datetrunc date, weightdiff float)
declare #dt1 datetime, #dt2 datetime
select #dt2 = max(datetrunc) from truck
select #dt1 = max(datetrunc) from truck where datetrunc < #dt2
select #dt1 [dt1], #dt2 [dt2]
SELECT t1.truckid, t2.weight - t1.weight [WeightDiff]
FROM truck t1
inner join truck t2 ON t1.truckid = t2.truckid
WHERE t1.datetrunc = #dt1
AND t2.datetrunc = #dt2
UPDATE truck SET WeightDiff = x.WeightDiff
FROM (
SELECT t1.truckid, t2.weight - t1.weight [WeightDiff]
FROM truck t1
inner join truck t2 ON t1.truckid = t2.truckid
WHERE t1.datetrunc = #dt1
AND t2.datetrunc = #dt2
) AS X
WHERE truck.datetrunc = #dt2
AND x.truckid = truck.truckid
SELECT t1.truckid, #dt2, t2.weight - t1.weight
FROM truck t1
inner join truck t2 ON t1.truckid = t2.truckid
WHERE t1.datetrunc = #dt1
AND t2.datetrunc = #dt2
I'm hoping for a difference between each date to show. However, it is only dsiplaying the difference between the latest dates, while deleting&null the other rows:
TruckID Weight datetrunc weightdiff
1 1000 2019-03-01 NULL
2 1111 2019-03-01 NULL
3 1222 2019-03-01 NULL
1 1050 2019-03-15 NULL
2 1700 2019-03-15 NULL
3 1400 2019-03-15 NULL
1 1125 2019-03-31 75
2 1725 2019-03-31 25
3 1600 2019-03-31 200
I want:
TruckID Weight datetrunc weightdiff
1 1000 2019-03-01 NULL
2 1111 2019-03-01 NULL
3 1222 2019-03-01 NULL
4 1400 2019-03-01 NULL
1 1050 2019-03-15 50
2 1700 2019-03-15 589
3 1400 2019-03-15 178
4 1490 2019-03-15 90
1 1125 2019-03-31 75
2 1725 2019-03-31 25
3 1600 2019-03-31 200
4 1900 2019-03-31 510
Notice how some TruckID 4 were completed deleted from my dataset. Also, how it replaces the weight difference.
I can't seem to figure how to make my data display properly. Any suggestions help, thanks!
For SQL Server 2008
select a.truckid, a.weight, a.datetrunc, (a.weight - c.weight) weightdiff
from truck a
outer apply (select top 1 weight from truck b where b.datetrunc<a.datetrunc and b.truckid=a.truckid order by b.datetrunc desc) c
Newer SQL Server
select truckid, weight, datetrunc, weight - lag(weight) over (partition by truckid order by datetrunc) weightdiff
from truck

How duplicate a rows in SQL base on difference between date columns and divided aggregated column per duplicate row?

I have a table with some records about fuel consumption. The important columns in the table are: CONSUME_DATE_FROM and CONSUM_DATE_TO.
I want to calculate average fuel consumption per cars on a monthly basis but some rows are not in the same month. For example some have a three month difference between them and the total of gas per litre is aggregated in a single row.
Now I should find records that have difference more than a month between CONSUME_DATE_FROM and CONSUM_DATE_TO, and duplicate them in current or second table per count of month and divide the total gas per litre between related rows.
I've this table with the following data:
ID VehicleId CONSUME_DATE_FROM CONSUM_DATE_TO GAS_PER_LITER
1 100 2018-10-25 2018-12-01 600
2 101 2018-07-19 2018-07-24 100
3 102 2018-12-31 2019-01-01 400
4 103 2018-03-29 2018-05-29 200
5 104 2018-02-05 2018-02-09 50
The expected output table should be as below
ID VehicleId CONSUME_DATE_FROM CONSUM_DATE_TO GAS_PER_LITER
1 100 2018-10-25 2018-12-01 200
1 100 2018-10-25 2018-12-01 200
1 100 2018-10-25 2018-12-01 200
2 101 2018-07-19 2018-07-24 100
3 102 2018-12-31 2019-01-01 200
3 102 2018-12-31 2019-01-01 200
4 103 2018-03-29 2018-05-29 66.66
4 103 2018-03-29 2018-05-29 66.66
4 103 2018-03-29 2018-05-29 66.66
5 104 2018-02-05 2018-02-09 50
Or as below
ID VehicleId CONSUME_DATE_FROM CONSUM_DATE_TO GAS_PER_LITER DATE_RELOAD_GAS
1 100 2018-10-25 2018-12-01 200 2018-10-01
1 100 2018-10-25 2018-12-01 200 2018-11-01
1 100 2018-10-25 2018-12-01 200 2018-12-01
2 101 2018-07-19 2018-07-24 100 2018-07-01
3 102 2018-12-31 2019-01-01 200 2018-12-01
3 102 2018-12-31 2019-01-01 200 2019-01-01
4 103 2018-03-29 2018-05-29 66.66 2018-03-01
4 103 2018-03-29 2018-05-29 66.66 2018-04-01
4 103 2018-03-29 2018-05-29 66.66 2018-05-01
5 104 2018-02-05 2018-02-09 50 2018-02-01
Can someone please help me out with this query?
I'm using oracle database
Your business rule treats the difference between CONSUME_DATE_FROM and CONSUM_DATE_TO as absolute months. So you expect the difference between 2018-10-25 and 2018-12-01 to be three months whereas the difference in days actually equates to about 1.1 months. So we can't use simple date arithmetic to get your desired output, we need to do some additional massaging of the dates.
The query below implements your desired logic by deriving the first day of the month for CONSUME_DATE_FROM and the last day of the month for CONSUME_DATE_TO, then using ceil() to round the difference up to the nearest whole number of months.
This is calculated in a subquery which is used in the main query with the old connect by level trick to multiply a record by level number of times:
with cte as (
select f.*
, ceil(months_between(last_day(CONSUM_DATE_TO)
, trunc(CONSUME_DATE_FROM,'mm'))) as diff
from fuel_consumption f
)
select cte.id
, cte.VehicleId
, cte.CONSUME_DATE_FROM
, cte.CONSUM_DATE_TO
, cte.GAS_PER_LITER/cte.diff as GAS_PER_LITER
, add_months(trunc(cte.CONSUME_DATE_FROM, 'mm'), level-1) as DATE_RELOAD_GAS
from cte
connect by level <= cte.diff
and prior cte.id = cte.id
and prior sys_guid() is not null
;
"what about if add a additional column "DATE_RELOAD_GAS" that display difference date for similar rows"
From your posted sample it seems like DATE_RELOAD_GAS is the first day of the month for each month bounded by CONSUME_DATE_FROM and CONSUM_DATE_TO. I have amended my solution to implement this rule.
By using connect by level structure with considering to_char(c.CONSUME_DATE_FROM + level - 1,'yyyymm') as month I was able to resolve as below :
select ID, VehicleId, myMonth, CONSUME_DATE_FROM, CONSUM_DATE_TO,
trunc(GAS_PER_LITER/max(rn) over (partition by ID order by ID),2) as GAS_PER_LITER,
'01.'||substr(myMonth,5,2)||'.'||substr(myMonth,1,4) as DATE_RELOAD_GAS
from
(
with consumption( ID, VehicleId, CONSUME_DATE_FROM, CONSUM_DATE_TO, GAS_PER_LITER ) as
(
select 1,100,date'2018-10-25',date'2018-12-01',600 from dual union all
select 2,101,date'2018-07-19',date'2018-07-24',100 from dual union all
select 3,102,date'2018-12-31',date'2019-01-01',400 from dual union all
select 4,103,date'2018-03-29',date'2018-05-29',200 from dual union all
select 5,104,date'2018-02-05',date'2018-02-09', 50 from dual
)
select ID, to_char(c.CONSUME_DATE_FROM + level - 1,'yyyymm') myMonth,
VehicleId, c.CONSUME_DATE_FROM, c.CONSUM_DATE_TO, GAS_PER_LITER,
row_number() over (partition by ID order by ID) as rn
from dual join consumption c
on c.ID >= 2
group by ID, to_char(c.CONSUME_DATE_FROM + level - 1,'yyyymm'), VehicleId,
c.CONSUME_DATE_FROM, c.CONSUM_DATE_TO, c.GAS_PER_LITER
connect by level <= c.CONSUM_DATE_TO - c.CONSUME_DATE_FROM + 1
union all
select ID, to_char(c.CONSUME_DATE_FROM + level - 1,'yyyymm') myMonth,
VehicleId, c.CONSUME_DATE_FROM, c.CONSUM_DATE_TO, GAS_PER_LITER,
row_number() over (partition by ID order by ID) as rn
from dual join consumption c
on c.ID = 1
group by ID, to_char(c.CONSUME_DATE_FROM + level - 1,'yyyymm'), VehicleId,
c.CONSUME_DATE_FROM, c.CONSUM_DATE_TO, c.GAS_PER_LITER
connect by level <= c.CONSUM_DATE_TO - c.CONSUME_DATE_FROM + 1
) q
group by ID, VehicleId, myMonth, CONSUME_DATE_FROM, CONSUM_DATE_TO, GAS_PER_LITER, rn
order by ID, myMonth;
I met an interesting issue that if I consider the join condition in the subquery as c.ID >= 1 query hangs on for huge period of time, so splitted into two parts by union all
as c.ID >= 2 and c.ID = 1
Rextester Demo

Opening Stock, Closing Stock By Date sql Query

I have two table with primary key and foreign key (MaterialId)
Material Table (Multiple Material)
MaterialId MaterialName OpeningStock
1 Pen 100
2 Pencil 50
Material Stock (Multiple Material Entry)
MaterialId PurchaseQty SalesQty Date
1 500 0 2016-12-15
1 0 0 2016-12-16
1 300 0 2016-12-17
1 0 400 2016-12-18
1 0 0 2016-12-19
1 0 0 2016-12-20
1 0 400 2016-12-21
1 200 100 2016-12-22
Now When I Pass #FromDate and #Todate
I want to output like below:
Date MaterialName OpeningStock PurchaseQty SalesQty ClosingStock
2016-12-15 Pen 100 500 0 600
2016-12-16 Pen 600 0 0 600
2016-12-17 Pen 600 300 0 900
2016-12-18 Pen 900 0 400 500
2016-12-19 Pen 500 0 0 500
2016-12-20 Pen 500 0 0 500
2016-12-21 Pen 500 0 400 100
2016-12-22 Pen 100 200 100 200
Note :
1. If Something is wrong on database tables so, please guide me how to handle this situation.
2. And Also find Current Date Stock From Two Tables
You are looking for a rolling sum of the various quantity values. One way to do this is using correlated subqueries:
SELECT
t1.Date,
mt.MaterialName,
(SELECT OpeningStock FROM [Material Table] WHERE MaterialId = t1.MaterialId) +
COALESCE((SELECT SUM(t2.PurchaseQty - t2.SalesQty) FROM [Material Stock] t2
WHERE t2.Date < t1.Date AND t1.MaterialId = t2.MaterialId), 0) AS OpeningStock,
t1.PurchaseQty,
t1.SalesQty,
(SELECT OpeningStock FROM [Material Table] WHERE MaterialId = t1.MaterialId) +
COALESCE((SELECT SUM(t2.PurchaseQty - t2.SalesQty) FROM [Material Stock] t2
WHERE t2.Date <= t1.Date AND t1.MaterialId = t2.MaterialId), 0) AS ClosingStock
FROM [Material Stock] t1
INNER JOIN [Material Table] mt
ON t1.MaterialId = mt.MaterialId
ORDER BY
mt.MaterialName,
t1.Date;
Note that it is bad table design to be storing the opening stock values in a separate table from the material stock table. This means the above query would return no pencil records. A better approach would be to insert a seed record into material stock, for each material, with the amount being the initial stock.
Output:
Demo here:
Rextester
Simply do as below :
SELECT S.DATE, M.MaterialName, M.OpeningStock, S.PurchaseQty, S.SalesQty, SUM((M.OpeningStock+S.PurchaseQty)-S.SalesQty)ClosingStock FROM #TABLE
(
SELECT * FROM MaterialTABLE
) M
INNER JOIN Material S ON S.MaterialId = M.MaterialId where s.date between #FromDate and #Todate

SQL Server: Calculate amortization

Currently i'm struggling to solve following problem:
I have two tables (contract & amortization).
Contract: contract informations with an outstanding amount
Amortization: date of payment & amortization amount
My desired result is a table with the contract and the decreasing
oustanding amount.
;WITH CTE AS
(
SELECT con.ID
,con.outstanding
,amo.amortization
,amo.amo_date
,ROW_NUMBER() OVER ( PARTITION BY con.ID
ORDER BY amo.amo_date asc
) as rn
FROM contract con
INNER JOIN amort amo
ON amo.contractID = con.ID
)
SELECT ID
,outstanding
,outstanding - amortization as new_outstanding
,amo_date
FROM CTE
Currently i am getting this result - which is of course wrong, as just one amortization is calculated for the new_outstanding:
ID outstanding new_outstanding amo_date
1 100000 90000 01.08.2017 00:00:00
1 100000 80000 01.09.2017 00:00:00
1 100000 50000 01.10.2017 00:00:00
1 100000 90000 01.11.2017 00:00:00
1 100000 90000 01.12.2017 00:00:00
My desired result would be:
ID outstanding new_outstanding amo_date
1 100000 90000 01.08.2017 00:00:00
1 100000 70000 01.09.2017 00:00:00
1 100000 20000 01.10.2017 00:00:00
1 100000 10000 01.11.2017 00:00:00
1 100000 0 01.12.2017 00:00:00
Any simple idea to solve this in an easy way?
Rextester: http://rextester.com/SBLT77863
Thanks in advance!
I think you just need a cumulative sum:
SELECT con.ID,
con.outstanding,
amo.amortization,
amo.amo_date,
(con.outstanding -
sum(amo.amortization) over (partition by amo.contractId
order by amo.amo_date)
) as new_outstanding
FROM contract con INNER JOIN
amort amo
ON amo.contractID = con.ID;

Sql for considering previous week same day sales if same day sales are 0

i have a store sales table which looks like below containing two years of data.
If the sales amount is '0' for a particular day for a particular store ,i have to take sales for same day last week.(current day-7)
if those sales are also '0' then sales for current_day-8
if those sales are also '0' then sales for current_day-9
if those sales are also '0' then sales for current_day-10
Sales_table1
day_id week_id sales Store
2/1/2014 201401 34566 1234
2/2/2014 201401 67777 567
2/3/2014 201401 3333 698
2/4/2014 201401 45644 345
2/5/2014 201401 2456 789
**2/6/2014 201401 3456 567**
2/7/2014 201401 5674 780
2/8/2014 201402 3333 1234
2/9/2014 201402 22222 567
2/10/2014 201402 111134 698
2/11/2014 201402 56789 345
2/12/2014 201402 4356 789
**2/13/2014 201402 0 567**
2/14/2014 201402 899 780
please give the query that i can use.
As you have not said which database you are using I'm giving a solution using SQL Server as that's what I'm most familiar with.
The logic of my query is to select all the rows where sales are greater than zero for each store, where the date of the sales info is either the MAX date in the table or the date is less than or equal to the MAX(date)-7 days (effectively exclude any sales in the previous 7 days). I'm doing this because you said you need to take the most recent sale or sales on day -7, -8, -9, etc.
From that set of rows the query then picks the most recent sale date.
;WITH Store_Dates AS (
SELECT store, MAX(day_id) max_date, DATEADD(day, -7, MAX(day_id)) old_date
FROM Sales_Table1
GROUP BY store
)
,Sale_Days AS (
SELECT MAX(day_id) day_id, Sales_Table1.store
FROM Sales_Table1 INNER JOIN Store_Dates
ON Sales_Table1.store = Store_Dates.store
WHERE sales > 0
AND (day_id = max_date
OR day_id <= old_date)
GROUP BY Sales_Table1.store
)
SELECT Sales_Table1.*
FROM Sales_Table1 INNER JOIN Sale_Days
ON Sales_Table1.store = Sale_Days.store
AND Sales_Table1.day_id = Sale_Days.day_id
SQL Fiddle
The only thing my query does not do is stop after going back X number of days. You could do that by limiting the number of rows returned from the Sale_Days query.