how to sum tow field at get inline result? - sql

i have tow field for example credit an debit in one table.
and i need to sum them and get result at each line for example :
date debit credit amount
2015/01/01 20 0 20
2015/01/02 0 5 15
2015/01/03 0 30 -15
i hope you help me to get the amount by a query
thanks

With SQL-Server 2012 or newer you can use this:
SELECT [date], debit, credit, amount,
SUM(debit-credit) OVER(ORDER BY [date] ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW) AS amount
FROM TableName
ORDER BY [date]
Read: OVER-clause, especially the ROWS | RANGE part
With other versions you have to use a correlated subquery:
SELECT [date], debit, credit, amount,
(SELECT SUM(debit-credit)
FROM TableName t2
WHERE [date] <= t1.[date]) AS amount
FROM TableName t1
ORDER BY [date]

I agree with Tim's answer, I added some extra lines:
declare #credit as table (
[date] datetime,
amount int
)
declare #debit as table (
[date] datetime,
amount int
)
insert into #debit values
('2015-01-01', 20)
insert into #credit values
('2015-01-02', 5),
('2015-01-03', 30)
select
[date], debit, credit, SUM(debit-credit) OVER(ORDER BY [date] ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW) AS amount
from(
select
[date], sum(debit) debit, sum(credit) credit
from
(
select
[date], 0 credit, d.amount debit
from
#debit d
union all
select
[date], c.amount credit, 0 debit
from
#credit c
) j group by j.date
) x

Related

Taking most recent values in sum over date range

I have a table which has the following columns: DeskID *, ProductID *, Date *, Amount (where the columns marked with * make the primary key). The products in use vary over time, as represented in the image below.
Table format on the left, and a (hopefully) intuitive representation of the data on the right for one desk
The objective is to have the sum of the latest amounts of products by desk and date, including products which are no longer in use, over a date range.
e.g. using the data above the desired table is:
So on the 1st Jan, the sum is 1 of Product A
On the 2nd Jan, the sum is 2 of A and 5 of B, so 7
On the 4th Jan, the sum is 1 of A (out of use, so take the value from the 3rd), 5 of B, and 2 of C, so 8 in total
etc.
I have tried using a partition on the desk and product ordered by date to get the most recent value and turned the following code into a function (Function1 below) with #date Date parameter
select #date 'Date', t.DeskID, SUM(t.Amount) 'Sum' from (
select #date 'Date', t.DeskID, t.ProductID, t.Amount
, row_number() over (partition by t.DeskID, t.ProductID order by t.Date desc) as roworder
from Table1 t
where 1 = 1
and t.Date <= #date
) t
where t.roworder = 1
group by t.DeskID
And then using a utility calendar table and cross apply to get the required values over a time range, as below
select * from Calendar c
cross apply Function1(c.CalendarDate)
where c.CalendarDate >= '20190101' and c.CalendarDate <= '20191009'
This has the expected results, but is far too slow. Currently each desk uses around 50 products, and the products roll every month, so after just 5 years each desk has a history of ~3000 products, which causes the whole thing to grind to a halt. (Roughly 30 seconds for a range of a single month)
Is there a better approach?
Change your function to the following should be faster:
select #date 'Date', t.DeskID, SUM(t.Amount) 'Sum'
FROM (SELECT m.DeskID, m.ProductID, MAX(m.[Date) AS MaxDate
FROM Table1 m
where m.[Date] <= #date) d
INNER JOIN Table1 t
ON d.DeskID=t.DeskID
AND d.ProductID=t.ProductID
and t.[Date] = d.MaxDate
group by t.DeskID
The performance of TVF usually suffers. The following removes the TVF completely:
-- DROP TABLE Table1;
CREATE TABLE Table1 (DeskID int not null, ProductID nvarchar(32) not null, [Date] Date not null, Amount int not null, PRIMARY KEY ([Date],DeskID,ProductID));
INSERT Table1(DeskID,ProductID,[Date],Amount)
VALUES (1,'A','2019-01-01',1),(1,'A','2019-01-02',2),(1,'B','2019-01-02',5),(1,'A','2019-01-03',1)
,(1,'B','2019-01-03',4),(1,'C','2019-01-03',3),(1,'B','2019-01-04',5),(1,'C','2019-01-04',2),(1,'C','2019-01-05',2)
GO
DECLARE #StartDate date=N'2019-01-01';
DECLARE #EndDate date=N'2019-01-05';
;WITH cte_p
AS
(
SELECT DISTINCT DeskID,ProductID
FROM Table1
WHERE [Date] <= #EndDate
),
cte_a
AS
(
SELECT #StartDate AS [Date], p.DeskID, p.ProductID, ISNULL(a.Amount,0) AS Amount
FROM (
SELECT t.DeskID, t.ProductID
, MAX(t.Date) AS FirstDate
FROM Table1 t
WHERE t.Date <= #StartDate
GROUP BY t.DeskID, t.ProductID) f
INNER JOIN Table1 a
ON f.DeskID=a.DeskID
AND f.ProductID=a.ProductID
AND f.[FirstDate]=a.[Date]
RIGHT JOIN cte_p p
ON p.DeskID=a.DeskID
AND p.ProductID=a.ProductID
UNION ALL
SELECT DATEADD(DAY,1,a.[Date]) AS [Date], t.DeskID, t.ProductID, t.Amount
FROM Table1 t
INNER JOIN cte_a a
ON t.DeskID=a.DeskID
AND t.ProductID=a.ProductID
AND t.[Date] > a.[Date]
AND t.[Date] <= DATEADD(DAY,1,a.[Date])
WHERE a.[Date]<#EndDate
UNION ALL
SELECT DATEADD(DAY,1,a.[Date]) AS [Date], a.DeskID, a.ProductID, a.Amount
FROM cte_a a
WHERE NOT EXISTS(SELECT 1 FROM Table1 t
WHERE t.DeskID=a.DeskID
AND t.ProductID=a.ProductID
AND t.[Date] > a.[Date]
AND t.[Date] <= DATEADD(DAY,1,a.[Date]))
AND a.[Date]<#EndDate
)
SELECT [Date], DeskID, SUM(Amount)
FROM cte_a
GROUP BY [Date], DeskID;

SQL Server group by date

SELECT [DATE], [AMOUNT], SUM(AMOUNT) OVER (ORDER BY DATE) AS 'Running Total'
FROM PeopleActi
WHERE INSTANCE = 'Bank'
AND DATE IS NOT NULL
GROUP BY [DATE], [AMOUNT];
In the code above I selecting a user's date, amount and the "SUM(AMOUNT) OVER (ORDER BY DATE) AS 'Running Total'" is the running total of their costs over a period of dates. When I run this code I get the following results:
DATE AMOUNT Running Total
2018-10-05 100 100
2018-10-06 1000 1100
2018-10-07 5000 6100
2018-10-08 2000 8100
2018-10-09 1000 9100
2018-10-10 5000 14100
2018-10-11 3000 25100
2018-10-11 8000 25100
This works nicely but my issue is the last two rows. I wanted them to be grouped by their date and have the total amount for both same days, so it should be:
Date Amount Running Total
2018-10-11 11000 25100
Does anyone have an idea of how this can achieved? My [DATE] is of type DATE.
UPDATE!!!!
I've seen some of your solutions and they are good but its important I display the AMOUNT and the Running Total amount as well, so the final result should be...
DATE AMOUNT Running Total
2018-10-05 100 100
2018-10-06 1000 1100
2018-10-07 5000 6100
2018-10-08 2000 8100
2018-10-09 1000 9100
2018-10-10 5000 14100
2018-10-11 11000 25100
Thank you everyone for the help so far!
Group up the amounts and then do your cumulative total
WITH CTE
AS
(
SELECT A.Dt,
SUM(A.Amount) AS Amount
FROM (
VALUES ('2018-10-05',100),
('2018-10-06',1000),
('2018-10-07',5000),
('2018-10-08',2000),
('2018-10-09',1000),
('2018-10-10',5000),
('2018-10-11',3000),
('2018-10-11',8000)
) AS A(Dt,Amount)
GROUP BY A.Dt
)
SELECT C.Dt,
C.Amount,
SUM(C.Amount) OVER (ORDER BY C.Dt) AS CumTotal
FROM CTE AS C;
Try like below
SELECT [DATE],sum( [AMOUNT]), SUM(AMOUNT) OVER (ORDER BY DATE) AS 'Running Total'
FROM PeopleActi
WHERE INSTANCE = 'Bank'
AND DATE IS NOT NULL
GROUP BY [DATE]
If you need groping sum then why you are using window function, only aggregation is enough :
SELECT [DATE], SUM([AMOUNT])
FROM PeopleActi
WHERE INSTANCE = 'Bank' AND DATE IS NOT NULL
GROUP BY [DATE];
Try this
;WITH CTe([DATE],AMOUNT)
AS
(
SELECT '2018-10-05', 100 UNION ALL
SELECT '2018-10-06', 1000 UNION ALL
SELECT '2018-10-07', 5000 UNION ALL
SELECT '2018-10-08', 2000 UNION ALL
SELECT '2018-10-09', 1000 UNION ALL
SELECT '2018-10-10', 5000 UNION ALL
SELECT '2018-10-11', 3000 UNION ALL
SELECT '2018-10-11', 8000
)
SELECT DISTINCT [DATE],SUM(AMOUNT)OVER(PARTITION BY [DATE] ORDER BY [DATE]) AMOUNT , SUM(AMOUNT)OVER( ORDER BY [DATE]) AS RuningTot FROM CTe
Script
SELECT DISTINCT [DATE],
SUM(AMOUNT)OVER(PARTITION BY [DATE] ORDER BY [DATE]) AS AMOUNT,
SUM(AMOUNT) OVER (ORDER BY DATE) AS 'Running Total'
FROM PeopleActi
WHERE INSTANCE = 'Bank'
AND DATE IS NOT NULL
I would use a CTE to first group by Date, and then do your running total ..
So something like
with myAmounts AS
(
SELECT [DATE], SUM([AMOUNT]) AS Amount
FROM PeopleActi
WHERE INSTANCE = 'Bank'
AND DATE IS NOT NULL
GROUP BY [DATE]
)
SELECT [DATE], [AMOUNT], SUM(AMOUNT) OVER (ORDER BY DATE) AS 'Running Total'
FROM myAmounts
GROUP BY [DATE], [AMOUNT]
;
HTH,
B
ps; just saw that its the same answer as another .. democoding in action
Every field in a group by is going to cause it to potentially create new lines. If you SUM the amount field and remove it from your grouping, that should solve the issue. EDIT: I see the issue, I provided a fully stand alone example of the query below that you can adapt.
DECLARE #PeopleActi TABLE ([DATE] DATE,[AMOUNT] MONEY)
INSERT INTO #PeopleActi SELECT '2018-10-05',100
INSERT INTO #PeopleActi SELECT '2018-10-06',1000
INSERT INTO #PeopleActi SELECT '2018-10-07',5000
INSERT INTO #PeopleActi SELECT '2018-10-08',2000
INSERT INTO #PeopleActi SELECT '2018-10-09',1000
INSERT INTO #PeopleActi SELECT '2018-10-10',5000
INSERT INTO #PeopleActi SELECT '2018-10-11',3000
INSERT INTO #PeopleActi SELECT '2018-10-11',8000
SELECT *, SUM(AMOUNT) OVER (ORDER BY DATE) AS 'Running Total'
FROM (
SELECT [DATE], SUM([AMOUNT]) AS AMOUNT
FROM #PeopleActi
WHERE DATE IS NOT NULL
GROUP BY [DATE]
) a
GROUP BY [DATE],Amount
Try Subselect:
SELECT p.[DATE], p.[AMOUNT], SUM(AMOUNT) OVER (ORDER BY DATE) AS 'Running Total'
FROM
(
select [date], sum([amount]) as Amount from PeopleActi
WHERE INSTANCE = 'Bank'
AND DATE IS NOT NULL
group by [date]
) p
GROUP BY p.[DATE], p.[AMOUNT]

Any one help me to solve this i try my best but did not solve this?

ItemName Price CreatedDateTime
New Card 50.00 2014-05-26 19:17:09.987
Recharge 110.00 2014-05-26 19:17:12.427
Promo 90.00 2014-05-27 16:17:12.427
Membership 70.00 2014-05-27 16:17:12.427
New Card 50.00 2014-05-26 19:20:09.987
Out Put : Need a query which Sum the sale of Current hour and
sale of item which have maximum sale in that hour in breakdownofSale
Column.
Hour SaleAmount BreakDownOfSale
19 210 Recharge
16 160 Promo
This should do it
create table #t
(
ItemName varchar(50),
Price decimal(18,2),
CreatedDateTime datetime
);
set dateformat ymd;
insert into #t values('New Card', 50.00, '2014-05-26 19:17:09.987');
insert into #t values('Recharge', 110.00, '2014-05-26 19:17:12.427');
insert into #t values('Promo', 90.00, '2014-05-27 16:17:12.427');
insert into #t values('Membership', 70.00, '2014-05-27 16:17:12.427');
insert into #t values('New Card', 50.00, '2014-05-26 19:20:09.987');
with cte as
(
select datepart(hh, CreatedDateTime) as [Hour],
ItemName,
Price,
sum(Price) over (partition by datepart(hh, CreatedDateTime)) SaleAmount,
ROW_NUMBER() over (partition by datepart(hh, CreatedDateTime) order by Price desc) rn
from #t
)
select Hour,
SaleAmount,
ItemName
from cte
where rn = 1
Though i am not clear with the question, based on your desired output, you may use the query as below.
SELECT DATEPART(HOUR,CreatedDateTime) AS Hour, sum(Price) AS Price, ItemName AS BreakDownOfSale from TableName WHERE BY ItemName,DATEPART(HOUR,CreatedDateTime)
Replace table name and column name with the actual one.
Hope this helps!
Here is the sample query.
You can use SQL Server Windows functions to get the result you need.
DECLARE #Table TABLE
(
ItemName NVARCHAR(40),
Price DECIMAL(10,2),
CreatedDatetime DATETIME
)
-- Fill table.
INSERT INTO #Table
( ItemName, Price, CreatedDatetime )
VALUES
( N'New Card' , 50.00 , '2014-05-26 19:17:09.987' ),
( N'Recharge' , 110.00 , '2014-05-26 19:17:12.427' ) ,
( N'Promo' , 90.00 , '2014-05-27 16:17:12.427' ) ,
( N'Membership' , 70.00 , '2014-05-27 16:17:12.427' ) ,
( N'New Card' , 50.00 , '2014-05-26 19:20:09.987' )
-- Check record(s).
SELECT * FROM #Table
-- Get record(s) in required way.
;WITH T1 AS
(
SELECT
DATEPART(HOUR, T.CreatedDatetime) AS Hour,
CONVERT(DATE, T.CreatedDatetime) AS Date,
T.ItemName AS BreakDownOfSales,
-- Date and hour both will give unique record(s)
SUM(Price) OVER (PARTITION BY CONVERT(DATE, T.CreatedDatetime), DATEPART(HOUR, CreatedDateTime)) AS SaleAmount,
ROW_NUMBER() OVER(PARTITION BY CONVERT(DATE, T.CreatedDatetime), DATEPART(HOUR, T.CreatedDatetime) ORDER BY T.Price DESC) AS RN
FROM
#Table T
)
SELECT
T1.Date ,
T1.Hour ,
T1.SaleAmount,
T1.BreakDownOfSales
FROM
T1
WHERE T1. RN = 1
ORDER BY
T1.Hour
Check this simple solution, Please convert it to SQL Server Query.
This will give you perfect result even if you have multiple date data.
SELECT HOUR(CreatedDateTime), SUM(Price),
(SELECT itemname FROM t it WHERE HOUR(ot.CreatedDateTime) = HOUR(it.CreatedDateTime) AND
DATE(ot.CreatedDateTime) = DATE(it.CreatedDateTime)
GROUP BY itemname
ORDER BY price DESC
LIMIT 1
) g
FROM t ot
GROUP BY HOUR(CreatedDateTime);

Get previous row updated value using LAG Without using Recursive CTE

How to use LAG function to get the updated previous row value (without using Recursive CTE). Please check the screenshot for sample output
Query Tried
Declare #Tbl as Table(SNO Int,Credit Money,Debit Money,PaidDate Date)
Insert into #Tbl
SELECT * FROM (VALUES (1,0,12,'7Jan16'), (2,10,0,'6Jan16'), (3,15,0,'5Jan16'), (4,0,5,'4Jan16'), (5,0,3,'3Jan16'), (6,0,2,'2Jan16'), (7,20,0,'1Jan16')) AS X(SNO,Credit,Debit,PaidDate)
Select
T.SNO,
T.Credit,
T.Debit,
TotalDebit = Case When Credit < LAG(T.Debit, 1, 0) OVER (ORDER BY SNO) Then Debit + (LAG(T.Debit, 1, 0) OVER (ORDER BY SNO)-Credit) Else Debit End,
Amount = Case When Credit < LAG(T.Debit, 1, 0) OVER (ORDER BY SNO) Then 0 Else Credit-LAG(T.Debit, 1, 0) OVER (ORDER BY SNO) End,
T.PaidDate
From #Tbl T
UPDATE:
Can get the expected result using recursive CTE, but when i convert the query to function and when i join the function with 3000 record, takes long time to execute. That's why i am trying to convert the query without recursive CTE part.
Recursive CTE Query:
Declare #Tbl as Table(SNO Int,Credit Money,Debit Money,PaidDate Date)
Insert into #Tbl
SELECT * FROM (VALUES (1,0,12,'7Jan16'), (2,10,0,'6Jan16'), (3,15,0,'5Jan16'), (4,0,5,'4Jan16'), (5,0,3,'3Jan16'), (6,0,2,'2Jan16'), (7,20,0,'1Jan16')) AS X(SNO,Credit,Debit,PaidDate)
;With Temp As(/* Detect Debited amount */
Select Top 1 SNO,Credit,Debit,Debit As TotalDebit,Credit As Amount,PaidDate From #Tbl
Union All
Select
R.SNO,
R.Credit,
R.Debit,
TotalDebit = Case When R.Credit < RP.TotalDebit Then R.Debit + (RP.TotalDebit-R.Credit) Else R.Debit End,
Amount = Case When R.Credit < RP.TotalDebit Then 0 Else R.Credit-RP.TotalDebit End,
R.PaidDate
From #Tbl R
Inner Join Temp RP ON R.SNO-1=RP.SNO
)
Select * From Temp
Spreadsheet sample:
https://docs.google.com/spreadsheets/d/1FNwzgGxmLiLFS_R5QANnfd16Iw64xhF0gWTc4ZocKsk/edit?usp=sharing
Performance here is suffering from recursive CTE. CTE on it's own is just syntactic sugar.
Just for this particular sample data this works without recursion:
Declare #Tbl as Table(SNO Int,Credit Money,Debit Money,PaidDate Date)
Insert into #Tbl
SELECT * FROM (VALUES (1,0,12,'7Jan16'), (2,10,0,'6Jan16'), (3,15,0,'5Jan16'), (4,0,5,'4Jan16'), (5,0,3,'3Jan16'), (6,0,2,'2Jan16'), (7,20,0,'1Jan16')) AS X(SNO,Credit,Debit,PaidDate);
With CTE1 As (
Select *
, CASE WHEN Credit > 0 THEN LEAD(1 - SIGN(Credit), 1, 1) OVER (ORDER BY SNO) ELSE 0 END As LastCrPerBlock
From #Tbl
), CTE2 As (
Select *
, SUM(LastCrPerBlock) OVER (ORDER BY SNO DESC ROWS UNBOUNDED PRECEDING) As BlockNumber
From CTE1
), CTE3 As (
Select *
, SUM(Credit - Debit) OVER (PARTITION BY BlockNumber) As BlockTotal
, SUM(Credit - Debit) OVER (PARTITION BY BlockNumber ORDER BY SNO ROWS UNBOUNDED PRECEDING) As BlockRunningTotal
From CTE2
)
Select SNO, Credit, Debit
, CASE WHEN BlockRunningTotal < 0 THEN -BlockRunningTotal ELSE 0 END As TotalDebit
, CASE WHEN BlockRunningTotal > 0 THEN CASE WHEN Credit < BlockRunningTotal THEN Credit ELSE BlockRunningTotal END ELSE 0 END As Amount
, PaidDate
From CTE3
Order By SNO;
This can help evaluate performance, but it will fail if in any block total of Debits exceed total of Credits. If BlockTotal is negative then it must be merged with one or several following blocks and that can't be done without iteration or recursion.
In real life I would dump CTE3 into temporary table and cycle over it merging blocks until there are no more negative BlockTotals.
From Y.B's answer, added recursive CTE to handle if any BlockTotal have negative. Cannot use while loop for recursion because i converted this query to inline table valued function.(Multi-statement table valued function is very slow)
Declare #Tbl as Table(ReceiptNo varchar(50),Credit Money,Debit Money,PaidDate Date)
Insert into #Tbl
SELECT * FROM (VALUES ('R1',20,0,'1Jan16'),('R2',0,2,'2Jan16'),('R3',0,3,'3Jan16'),('R4',0,5,'4Jan16'),('R5',10,0,'5Jan16'),('R6',0,1,'6Jan16'),('R7',0,10,'7Jan16')) AS X(ReceiptNo,Credit,Debit,PaidDate);
With Receipts As (
Select
SNO = ROW_NUMBER() OVER(ORDER BY PaidDate Desc),ReceiptNo,Credit,Debit,PaidDate,
LastCrPerBlock = CASE WHEN Credit > 0 THEN LEAD(1 - SIGN(Credit), 1, 1) OVER (ORDER BY PaidDate DESC) ELSE 0 END
From #Tbl
), Blocks As (
Select *
, SUM(LastCrPerBlock) OVER (ORDER BY SNO DESC ROWS UNBOUNDED PRECEDING) As BlockNumber
From Receipts
), BlockTotal As (
Select *
, SUM(Credit - Debit) OVER (PARTITION BY BlockNumber) As BlockTotal
, SUM(Credit - Debit) OVER (PARTITION BY BlockNumber ORDER BY SNO ROWS UNBOUNDED PRECEDING) As BlockRunningTotal
From Blocks
),
ReceiptAmount As (
Select ReceiptNo,
Amount = CASE WHEN BlockRunningTotal > 0 THEN CASE WHEN Credit < BlockRunningTotal THEN Credit ELSE BlockRunningTotal END ELSE 0 END,
Debit = IIF(BlockNumber<>LEAD(BlockNumber) OVER(ORDER BY SNO) and BlockRunningTotal<0,ABS(BlockRunningTotal),0),
PaidDate
From BlockTotal
),
FinalReceipt2012 As (
Select
SNO = ROW_NUMBER() OVER(ORDER BY PaidDate Desc),ReceiptNo,Amount,Debit,PaidDate,
Recur = IIF(Exists(Select Top 1 R1.Amount From ReceiptAmount R1 Where Debit>0),1,0)
From ReceiptAmount
Where Amount>0 or Debit>0
),
FinalReceipt As (
Select * From FinalReceipt2012 Where Recur=0 OR SNO=1
Union All
Select
R.SNO,R.ReceiptNo,
Amount = Case When R.Amount < RP.Debit Then 0 Else R.Amount-RP.Debit End,
Debit = Case When R.Amount < RP.Debit Then R.Debit + (RP.Debit-R.Amount) Else R.Debit End,
R.PaidDate,0 As Recur
From FinalReceipt2012 R
Inner Join FinalReceipt RP ON R.SNO=RP.SNO+1
Where R.Recur=1
)
Select ReceiptNo,Amount,PaidDate From FinalReceipt Where Amount>0
Input:
Output:

Debit, Credit not showing correct result on Same Date

Scenario : I am working on Users Accounts where Users add amount to there account (Credit) and they withdraw their desire amount from their account (Debit), all is going correct but when User credit or debit on same dates it gives me wrong result (Balance). here refno is reference of user. here is my Query
declare #startdate date='2013-01-02',
#enddate date='2013-01-12';
With summary(id,refno,transdate,cr,dr,balance)
as
(
select id,
RefNo,
Cast(TransDate as Varchar),
cr,
dr,
(cr-dr)+( Select ISNULL(Sum(l.Cr-l.Dr) ,0)
From Ledger l
Where l.TransDate<Ledger.TransDate and refno=001 ) as balance
from Ledger
),
openingbalance(id,refno,transdate,cr,dr,balance)
As (
select top 1 '' id,'OPENING BAL','','','',balance
from summary
where transdate<#startdate
order by transdate desc
)
select *
from openingbalance
union
Select *
From summary
where transdate between #startdate and #enddate and refno=001 order by transdate
If you are using SQL 2012 or above, then instead of
SELECT id, RefNo, TransDate,cr, dr, (cr-dr) + (Select ISNULL(Sum(l.Cr-l.Dr) ,0)
FROM Ledger l
WHERE Cast(l.TransDate as datetime) < Cast(Ledger.TransDate as datetime)
AND refno=001) as balance from Ledger
Use:
SELECT id, RefNo, TransDate, cr, dr, SUM(cr- dr) OVER(ORDER BY TransDate ROWS
BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW) AS balance
The issue is because when you query for the previous balance you are only looking at records that have a transdate earlier than the current record, so this means any records that have the same date will not be included.
The solution here would be to use a more unique sequential value, in your example you could use the ID value as the sequential identifier instead. However, ID values are not always the best for ensuring sequence. I would recommend extending your transdate column to use a more precise value and include the time of the transactions. Seconds would likely be enough precision if you can guarantee that there will never be multiple transactions made within a given second, but whatever you decide you need to be confident there will not be any duplicates.
In an attempt to provide a code change solution that will work with your existing data you can try the following, which uses the id value to determine if a record is prior to the current record:
Change the following line:
Where l.TransDate<Ledger.TransDate and refno=001 ) as balance
to this:
Where l.ID<Ledger.ID and refno=001 ) as balance
After hint by #musefan i made changes to query and it is working as i want. here is query for Date Base
declare #startdate date='2013-01-02',
#enddate date='2013-01-12';
With summary(id,refno,transdate,cr,dr,balance)
as
(
select id,
RefNo,
TransDate,
cr,
dr,
(cr-dr)+( Select ISNULL(Sum(l.Cr-l.Dr) ,0)
From Ledger l
Where Cast(l.TransDate as datetime)< Cast(Ledger.TransDate as datetime) and refno=001 ) as balance
from Ledger
),
openingbalance(id,refno,transdate,cr,dr,balance)
As (
select top 1 '' id,'OPENING BAL','','','',balance
from summary
where transdate<#startdate
order by transdate desc
)
select id,refno,Cast(TransDate as varchar) as datetime,cr,dr,balance
from openingbalance
union
Select id,refno,Cast(TransDate as varchar)as datetime,cr,dr,balance
From summary
where transdate between #startdate and #enddate and refno=001 order by Cast(TransDate as varchar)
and Another Query Id Based
declare #startdate date='2013-01-02',
#enddate date='2013-01-12';
With summary(id,refno,transdate,cr,dr,balance)
as
(
select id,
RefNo,
TransDate,
cr,
dr,
(cr-dr)+( Select ISNULL(Sum(l.Cr-l.Dr) ,0)
From Ledger l
Where l.id < Ledger.id and refno=001 ) as balance
from Ledger
),
openingbalance(id,refno,transdate,cr,dr,balance)
As (
select top 1 '' id,'OPENING BAL','','','',balance
from summary
where transdate<#startdate
order by transdate desc
)
select id,refno,Cast(TransDate as varchar) as datetime,cr,dr,balance
from openingbalance
union
Select id,refno,Cast(TransDate as varchar)as datetime,cr,dr,balance
From summary
where transdate between #startdate and #enddate and refno=001 order by id