query on mutiple transaction on same day - sql

I am using sql server 2012..i have to get running balance on each row..now i am using below query
CREATE TABLE #Test(ID INT,TransDate datetime,Credit MONEY,[Debit] money)
INSERT INTO #Test
SELECT 1,'01/01/2017',10000,NULL UNION
SELECT 1,'01/05/2017',40000,NULL UNION
SELECT 1,'01/05/2017',200,NULL UNION
SELECT 1,'01/05/2017',200,NULL UNION
SELECT 1,'01/09/2017',NULL,45000 UNION
SELECT 2,'01/05/2017',1000,NULL UNION
SELECT 2,'01/06/2017',1000,NULL
SELECT t1.ID,convert(varchar,t2.TransDate,103) 'date',
t2.Credit,
t2.Debit,
SUM(COALESCE(t1.credit, 0) - COALESCE(t1.debit, 0)) AS Balance
FROM #Test t1
INNER JOIN #Test t2
ON t1.TransDate <= t2.TransDate
GROUP BY t1.ID,t2.TransDate , t2.Credit, t2.Debit
Output:
1 01/01/2017 10000.00 NULL 10000.00
1 05/01/2017 200.00 NULL 50200.00
1 05/01/2017 1000.00 NULL 50200.00
1 05/01/2017 40000.00 NULL 50200.00
1 06/01/2017 1000.00 NULL 50200.00
1 09/01/2017 NULL 45000.00 5200.00
2 05/01/2017 200.00 NULL 1000.00
2 05/01/2017 1000.00 NULL 1000.00
2 05/01/2017 40000.00 NULL 1000.00
2 06/01/2017 1000.00 NULL 2000.00
2 09/01/2017 NULL 45000.00 2000.00
Require Output:
Output:
1 01/01/2017 10000.00 NULL 10000.00
1 05/01/2017 200.00 NULL 10200.00
1 05/01/2017 1000.00 NULL 11200.00
1 05/01/2017 40000.00 NULL 50200.00
1 06/01/2017 1000.00 NULL 51200.00
1 09/01/2017 NULL 45000.00 5200.00
2 05/01/2017 200.00 NULL 200.00
2 05/01/2017 1000.00 NULL 1200.00
2 05/01/2017 40000.00 NULL 41200.00
2 06/01/2017 1000.00 NULL 42200.00
But if multiple entries for same tran day query does not work(see 2nd ,3rd & 4th row result).. it should not same ..Please any one help me to get the proper result?

Use a window function. This will work if the date column contains datetime...
select id,
TransDate,
credit,
debit,
sum(coalesce(credit,0)-coalesce(debit,0))
over (partition by id order by TransDate) as RunningBalance
from #Test
If the date column is date only, use:
with t1 as
(
select id,
TransDate,
credit,
debit,
row_number() over(partition by id, Transdate order by credit) as t_ord
from #Test
)
select id,
TransDate,
credit,
debit,
sum(coalesce(credit,0)-coalesce(debit,0))
over (partition by id order by TransDate, t_ord) as RunningBalance
from t1

First get date with required column and grop by date:
select A.TransDate, SUM(COALESCE(credit, 0)) as credit, SUM(COALESCE(debit, 0)) as debit, SUM(COALESCE(credit, 0) - COALESCE(debit, 0)) AS Balance from
(
SELECT convert(varchar,t2.TransDate, 103) as TransDate, t2.Credit, t2.Debit
FROM Test t1
INNER JOIN Test t2
ON t1.TransDate <= t2.TransDate
) as A group by A.TransDate
=================Or===============
With temp table:
select A.TransDate, SUM(COALESCE(credit, 0)) as credit, SUM(COALESCE(debit, 0)) as debit, SUM(COALESCE(credit, 0) - COALESCE(debit, 0)) AS Balance from
(
SELECT convert(varchar,t2.TransDate, 103) as TransDate, t2.Credit, t2.Debit
FROM Test #t1
INNER JOIN #Test t2
ON t1.TransDate <= t2.TransDate
) as A group by A.TransDate

Related

Using EXISTS within a GROUP BY clause

Is it possible to do the following:
I have a table that looks like this:
declare #tran_TABLE TABLE(
EOMONTH DATE,
AccountNumber INT,
CLASSIFICATION_NAME VARCHAR(50),
Value Float
)
INSERT INTO #tran_TABLE VALUES('2018-11-30','123','cat1',10)
INSERT INTO #tran_TABLE VALUES('2018-11-30','123','cat1',15)
INSERT INTO #tran_TABLE VALUES('2018-11-30','123','cat1',5 )
INSERT INTO #tran_TABLE VALUES('2018-11-30','123','cat2',10)
INSERT INTO #tran_TABLE VALUES('2018-11-30','123','cat3',12)
INSERT INTO #tran_TABLE VALUES('2019-01-31','123','cat1',5 )
INSERT INTO #tran_TABLE VALUES('2019-01-31','123','cat2',10)
INSERT INTO #tran_TABLE VALUES('2019-01-31','123','cat2',15)
INSERT INTO #tran_TABLE VALUES('2019-01-31','123','cat3',5 )
INSERT INTO #tran_TABLE VALUES('2019-01-31','123','cat3',2 )
INSERT INTO #tran_TABLE VALUES('2019-03-31','123','cat1',15)
EOMONTH AccountNumber CLASSIFICATION_NAME Value
2018-11-30 123 cat1 10
2018-11-30 123 cat1 15
2018-11-30 123 cat1 5
2018-11-30 123 cat2 10
2018-11-30 123 cat3 12
2019-01-31 123 cat1 5
2019-01-31 123 cat2 10
2019-01-31 123 cat2 15
2019-01-31 123 cat3 5
2019-01-31 123 cat3 2
2019-03-31 123 cat1 15
I want to produce a result where it will check whether in each month, for each AccountNumber (just one in this case) there exists a CLASSIFICATION_NAME cat1, cat2, cat3.
If all 3 exist for the month, then return 1 but if any are missing return 0.
The result should look like:
EOMONTH AccountNumber CLASSIFICATION_NAME
2018-11-30 123 1
2019-01-31 123 1
2019-03-31 123 0
But I want to do it as compactly as possible, without first creating a table that groups everything by CLASSIFICATION_NAME, EOMONTH and AccountNumber and then selects from that table.
For example, in the pseudo code below, is it possible to use maybe an EXISTS statement to do the group by?
SELECT
EOMONTH
,AccountNumber
,CASE WHEN EXISTS (CLASSIFICATION_NAME = 'cat1' AND 'cat2' AND 'cat3') THEN 1 ELSE 0 end
,SUM(Value) AS totalSpend
FROM #tran_TABLE
GROUP BY
EOMONTH
,AccountNumber
You could emulate this behavior by counting the distinct classifications that answer this condition (per group):
SELECT
EOMONTH
,AccountNumber
,CASE COUNT(DISTINCT CASE WHEN classification_name IN ('cat1', 'cat2', 'cat3') THEN classification_name END)
WHEN 3 THEN 1
ELSE 0
END
,SUM(Value) AS totalSpend
FROM #tran_TABLE
GROUP BY
EOMONTH
,AccountNumber
Try this-
SELECT EOMONTH,
AccountNumber,
CASE
WHEN COUNT(DISTINCT CLASSIFICATION_NAME) = 3 THEN 1
ELSE 0
END CLASSIFICATION_NAME
FROM #tran_TABLE
GROUP BY EOMONTH,AccountNumber
Output is-
2018-11-30 123 1
2019-01-31 123 1
2019-03-31 123 0
Query like this. You can count distinct values.
When you count unique values then column 'Three_Unique_Cat'. When you count exactly 'cat1','cat2','cat3' then column 'Three_Cat1_Cat2_Cat3'
SELECT
EOMONTH, AccountNumber
,CASE WHEN
COUNT(DISTINCT CLASSIFICATION_NAME)=3 THEN 1
ELSE 0
END AS 'Three_Unique_Cat'
,CASE WHEN
COUNT(DISTINCT CASE WHEN CLASSIFICATION_NAME IN ('cat1','cat2','cat3')
THEN CLASSIFICATION_NAME ELSE NULL END)=3 THEN 1
ELSE 0
END AS 'Three_Cat1_Cat2_Cat3'
,SUM(Value) AS totalSpend
FROM #tran_TABLE
GROUP BY EOMONTH, AccountNumber
Output:
EOMONTH AccountNumber Three_Unique_Cat Three_Cat1_Cat2_Cat3 totalSpend
2018-11-30 123 1 1 52
2019-01-31 123 1 1 37
2019-03-31 123 0 0 15
It's easy, just as below:
select
EOMONTH,
AccountNumber,
case when count(distinct CLASSIFICATION_NAME) = 3 then 1 else 0 end as CLASSIFICATION_NAME
from
tran_TABLE
group by
EOMONTH,
AccountNumber

Sql Pivot on time

Table 1: Daily attendance data:
att_date emp_code emp_name in_time out_time
2018-10-21 9999 Test 2018-10-21 08:00:00.000 2018-10-22 06:00:00.000
Table 2: Trnevents
emp_readr_id DT EVENTID
9999 2018-10-24 07:00:00.000 0
9999 2018-10-24 05:00:00.000 0
9999 2018-10-24 03:00:00.000 0
9999 2018-10-23 21:00:00.000 0
9999 2018-10-23 19:00:00.000 0
9999 2018-10-23 06:00:00.000 0
9999 2018-10-22 06:00:00.000 0
9999 2018-10-21 08:00:00.000 0
I used this query to get all times in between in time and out time ,below query works fine but i try to make in row by using pivot. While using pivot out time shows in next row.
declare #tempProcesstable as table(
[id] [nvarchar](200) NULL,
[time_stamp] datetime NULL,
[AccessType] varchar(3) NULL)
insert into #tempProcesstable
select distinct t1.emp_Reader_id, t1.DT,t1.eventid from daily_attendance_data t2 join trnevents t1
on t1.emp_reader_id=t2.emp_reader_id where (CONVERT(VARCHAR(26), t2.att_Date, 23) >=CONVERT(VARCHAR(26), '2018-10-20', 23)
and CONVERT(VARCHAR(26), t2.att_date, 23) <=CONVERT(VARCHAR(26), '2018-10-21', 23))
and
(t1.DT >=t2.in_time
and t1.DT <=t2.out_time)
-- and t1.emp_reader_id=1000
group by t1.emp_Reader_id,t1.dt,t1.eventid order by t1.emp_reader_id,DT asc
; With CheckIns
As (Select Rowemp_reader_id = Row_Number() Over (Partition by id, Cast(time_stamp As Date) Order By time_stamp),
id, time_stamp,
[Date] = Cast(time_stamp As Date),
[Time] = Cast(time_stamp As Time(0))
From #tempProcesstable)
Select Pvt.id,B.emp_name , [Date], CHECK1, CHECK2,Cast(dateadd(ss,datediff(ss,CHECK1,CHECK2),0) As Time(0)) Total1,
CHECK3, CHECK4,Cast(dateadd(ss,datediff(ss,CHECK3,CHECK4),0) As Time(0)) Total2
From (Select id, [Date], [Time],
CHECKNum = 'CHECK' + Cast(Rowemp_reader_id As varchar(11))
From CheckIns) As P
Pivot (Min([Time])
For CheckNum In (Check1, [Check2], Check3, Check4)
) As Pvt
LEFT OUTER JOIN
dbo.employee AS B ON Pvt.id= B.emp_reader_id
My output:
id emp_name Date CHECK1 CHECK2 Total1 CHECK3 CHECK4 Total2
1048 Singh 2018-10-21 07:06:07 17:34:05 10:27:58 NULL NULL NULL
9999 Test 2018-10-21 08:00:00 NULL NULL NULL NULL NULL NULL
9999 Test 2018-10-22 06:00:00 NULL NULL NULL NULL NULL NULL
Expected output:
I want all times between in time and out time in night to morning also.
can any one help me to rectify this.
id emp_name Date CHECK1 CHECK2 Total1 CHECK3 CHECK4 Total2
1048 Singh 2018-10-21 07:06:07 17:34:05 10:27:58 NULL NULL NULL
9999 Test 2018-10-21 08:00:00 06:00:00 NULL NULL NULL NULL NULL
You can try to use ROW_NUMBER() window function make row number by each date.
then use condition aggregate function to do pivot
SELECT emp_readr_id,
emp_name,
[Date],
MAX(CASE WHEN RN = 1 THEN time END) CHECK1,
MAX(CASE WHEN RN = 2 THEN time END) CHECK2,
MAX(CASE WHEN RN = 3 THEN time END) CHECK3,
MAX(CASE WHEN RN = 4 THEN time END) CHECK4
FROM (
SELECT emp_readr_id,
emp_name,
CONVERT(VARCHAR(10),DT,120) 'Date',
ROW_NUMBER() OVER(PARTITION BY CONVERT(VARCHAR(10),DT,120) ORDER BY DT) rn,
CONVERT(VARCHAR(10),DT,108) time
FROM Daily d
JOIN Trnevents t on t.DT between d.in_time and d.out_time
) t1
group by emp_readr_id,
emp_name,
[Date]
sqlifddle

Sql group by latest repeated field

I don't even know what's a good title for this question.
But I'm having a table:
create table trans
(
[transid] INT IDENTITY (1, 1) NOT NULL,
[customerid] int not null,
[points] decimal(10,2) not null,
[date] datetime not null
)
and records:
--cus1
INSERT INTO trans ( customerid , points , date )
VALUES ( 1, 10, '2016-01-01' ) , ( 1, 20, '2017-02-01' ) , ( 1, 22, '2017-03-01' ) ,
( 1, 24, '2018-02-01' ) , ( 1, 50, '2018-02-25' ) , ( 2, 44, '2016-02-01' ) ,
( 2, 20, '2017-02-01' ) , ( 2, 32, '2017-03-01' ) , ( 2, 15, '2018-02-01' ) ,
( 2, 10, '2018-02-25' ) , ( 3, 10, '2018-02-25' ) , ( 4, 44, '2015-02-01' ) ,
( 4, 20, '2015-03-01' ) , ( 4, 32, '2016-04-01' ) , ( 4, 15, '2016-05-01' ) ,
( 4, 10, '2017-02-25' ) , ( 4, 10, '2018-02-27' ) ,( 4, 20, '2018-02-28' ) ,
( 5, 44, '2015-02-01' ) , ( 5, 20, '2015-03-01' ) , ( 5, 32, '2016-04-01' ) ,
( 5, 15, '2016-05-01' ) ,( 5, 10, '2017-02-25' );
-- selecting the data
select * from trans
Produces:
transid customerid points date
----------- ----------- --------------------------------------- -----------------------
1 1 10.00 2016-01-01 00:00:00.000
2 1 20.00 2017-02-01 00:00:00.000
3 1 22.00 2017-03-01 00:00:00.000
4 1 24.00 2018-02-01 00:00:00.000
5 1 50.00 2018-02-25 00:00:00.000
6 2 44.00 2016-02-01 00:00:00.000
7 2 20.00 2017-02-01 00:00:00.000
8 2 32.00 2017-03-01 00:00:00.000
9 2 15.00 2018-02-01 00:00:00.000
10 2 10.00 2018-02-25 00:00:00.000
11 3 10.00 2018-02-25 00:00:00.000
12 4 44.00 2015-02-01 00:00:00.000
13 4 20.00 2015-03-01 00:00:00.000
14 4 32.00 2016-04-01 00:00:00.000
15 4 15.00 2016-05-01 00:00:00.000
16 4 10.00 2017-02-25 00:00:00.000
17 4 10.00 2018-02-27 00:00:00.000
18 4 20.00 2018-02-28 00:00:00.000
19 5 44.00 2015-02-01 00:00:00.000
20 5 20.00 2015-03-01 00:00:00.000
21 5 32.00 2016-04-01 00:00:00.000
22 5 15.00 2016-05-01 00:00:00.000
23 5 10.00 2017-02-25 00:00:00.000
I'm trying to group all the customerid and sum their points. But here's the catch, If the trans is not active for 1 year(the next tran is 1 year and above), the points will be expired.
For this case:
Points for each customers should be:
Customer1 20+22+24+50
Customer2 20+32+15+10
Customer3 10
Customer4 10+20
Customer5 0
Here's what I have so far:
select
t1.transid as transid1,
t1.customerid as customerid1,
t1.date as date1,
t1.points as points1,
t1.rank1 as rank1,
t2.transid as transid2,
t2.customerid as customerid2,
t2.points as points2,
isnull(t2.date,getUTCDate()) as date2,
isnull(t2.rank2,t1.rank1+1) as rank2,
cast(case when(t1.date > dateadd(year,-1,isnull(t2.date,getUTCDate()))) Then 0 ELSE 1 END as bit) as ShouldExpire
from
(
select transid,CustomerID,Date,points,
RANK() OVER(PARTITION BY CustomerID ORDER BY date ASC) AS RANK1
from trans
)t1
left join
(
select transid,CustomerID,Date,points,
RANK() OVER(PARTITION BY CustomerID ORDER BY date ASC) AS RANK2
from trans
)t2 on t1.RANK1=t2.RANK2-1
and t1.customerid=t2.customerid
which gives
from the above table,how do I check for ShouldExpire field having max(rank1) for customer, if it's 1, then totalpoints will be 0, otherwise,sum all the consecutive 0's until there are no more records or a 1 is met?
Or is there a better approach to this problem?
The following query uses LEAD to get the date of the next record withing the same CustomerID slice:
;WITH CTE AS (
SELECT transid, CustomerID, [Date], points,
LEAD([Date]) OVER (PARTITION BY CustomerID
ORDER BY date ASC) AS nextDate,
CASE
WHEN [date] > DATEADD(YEAR,
-1,
-- same LEAD() here as above
ISNULL(LEAD([Date]) OVER (PARTITION BY CustomerID
ORDER BY date ASC),
getUTCDate()))
THEN 0
ELSE 1
END AS ShouldExpire
FROM trans
)
SELECT transid, CustomerID, [Date], points, nextDate, ShouldExpire
FROM CTE
ORDER BY CustomerID, [Date]
Output:
transid CustomerID Date points nextDate ShouldExpire
-------------------------------------------------------------
1 1 2016-01-01 10.00 2017-02-01 1 <-- last exp. for 1
2 1 2017-02-01 20.00 2017-03-01 0
3 1 2017-03-01 22.00 2018-02-01 0
4 1 2018-02-01 24.00 2018-02-25 0
5 1 2018-02-25 50.00 NULL 0
6 2 2016-02-01 44.00 2017-02-01 1 <-- last exp. for 2
7 2 2017-02-01 20.00 2017-03-01 0
8 2 2017-03-01 32.00 2018-02-01 0
9 2 2018-02-01 15.00 2018-02-25 0
10 2 2018-02-25 10.00 NULL 0
11 3 2018-02-25 10.00 NULL 0 <-- no exp. for 3
12 4 2015-02-01 44.00 2015-03-01 0
13 4 2015-03-01 20.00 2016-04-01 1
14 4 2016-04-01 32.00 2016-05-01 0
15 4 2016-05-01 15.00 2017-02-25 0
16 4 2017-02-25 10.00 2018-02-27 1 <-- last exp. for 4
17 4 2018-02-27 10.00 2018-02-28 0
18 4 2018-02-28 20.00 NULL 0
19 5 2015-02-01 44.00 2015-03-01 0
20 5 2015-03-01 20.00 2016-04-01 1
21 5 2016-04-01 32.00 2016-05-01 0
22 5 2016-05-01 15.00 2017-02-25 0
23 5 2017-02-25 10.00 NULL 1 <-- last exp. for 5
Now, you seem to want to calculate the sum of points after the last expiration.
Using the above CTE as a basis you can achieve the required result with:
;WITH CTE AS (
... above query here ...
)
SELECT CustomerID,
SUM(CASE WHEN rnk = 0 THEN points ELSE 0 END) AS sumOfPoints
FROM (
SELECT transid, CustomerID, [Date], points, nextDate, ShouldExpire,
SUM(ShouldExpire) OVER (PARTITION BY CustomerID ORDER BY [Date] DESC) AS rnk
FROM CTE
) AS t
GROUP BY CustomerID
Output:
CustomerID sumOfPoints
-----------------------
1 116.00
2 77.00
3 10.00
4 30.00
5 0.00
Demo here
The tricky part here is to dump all points when they expire, and start accumulating them again. I assumed that if there was only one transaction that we don't expire the points until there's a new transaction, even if that first transaction was over a year ago now?
I also get a different answer for customer #5, as they do appear to have a "transaction chain" that hasn't expired?
Here's my query:
WITH ordered AS (
SELECT
*,
ROW_NUMBER() OVER (PARTITION BY customerid ORDER BY [date]) AS order_id
FROM
trans),
max_transid AS (
SELECT
customerid,
MAX(transid) AS max_transid
FROM
trans
GROUP BY
customerid),
not_expired AS (
SELECT
t1.customerid,
t1.points,
t1.[date] AS t1_date,
CASE
WHEN m.customerid IS NOT NULL THEN GETDATE()
ELSE t2.[date]
END AS t2_date
FROM
ordered t1
LEFT JOIN ordered t2 ON t2.customerid = t1.customerid AND t1.transid != t2.transid AND t2.order_id = t1.order_id + 1 AND t1.[date] > DATEADD(YEAR, -1, t2.[date])
LEFT JOIN max_transid m ON m.customerid = t1.customerid AND m.max_transid = t1.transid
),
max_not_expired AS (
SELECT
customerid,
MAX(t1_date) AS max_expired
FROM
not_expired
WHERE
t2_date IS NULL
GROUP BY
customerid)
SELECT
n.customerid,
SUM(n.points) AS points
FROM
not_expired n
LEFT JOIN max_not_expired m ON m.customerid = n.customerid
WHERE
ISNULL(m.max_expired, '19000101') < n.t1_date
GROUP BY
n.customerid;
It could be refactored to be simpler, but I wanted to show the steps to get to the final answer:
customerid points
1 116.00
2 77.00
3 10.00
4 30.00
5 57.00
can you try this:
SELECT customerid,
Sum(t1.points)
FROM trans t1
WHERE NOT EXISTS (SELECT 1
FROM trans t2
WHERE Datediff(year, t1.date, t2.date) >= 1)
GROUP BY t1.customerid
Hope it helps!
try this:
select customerid,Sum(points)
from trans where Datediff(year, date, GETDATE()) < 1
group by customerid
output:
customerid Points
1 - 74.00
2 - 25.00
3 - 10.00
4 - 30.00

SQL query for calculating mtd, ytd values

I have a table with columns ID, Title, Date, Amount.
I need to get MTD, YTD amount values against each transaction based on Title, Date.
Is there anyone who had done this before?
Select t.title, t.Date,
Sum(y.Amount) YTD,
Sum(m.Amount) MTD
From table t
left join table y
on y.Title = t.Title
and datediff(year, y.Date, t.Date) = 0
and y.Date <= t.Date
left join table m
on m.Title = t.Title
and datediff(month, m.Date, t.Date) = 0
and m.Date <= t.Date
and m.Date <> y.Date
Group by t.title, t.Date
The accepted solution is incorrect. Let's say we have a following table:
ID Title Date Amount
--- ------ ---------- ------
1 Cust A 2020-01-01 2.00
2 Cust A 2020-01-05 3.00
3 Cust A 2020-02-01 5.00
The accepted answer would give us these results:
Title Date YTD MTD
------ ---------- ----- -----
Cust A 2021-01-01 2.00 2.00
Cust A 2021-01-05 10.00 10.00
Cust A 2021-02-01 10.00 15.00
This is due to the fact that each join multiplies the number of records by the number of matching records. This can be easily seen when the aggregation is removed:
Select t.title, t.Date, y.Date, m.Date,
y.Amount,
m.Amount
From [table] t
join [table] y
on y.Title = t.Title
and datediff(year, y.Date, t.Date) = 0
and y.Date <= t.Date
join [table] m
on m.Title = t.Title
and datediff(month, m.Date, t.Date) = 0
and m.Date <= t.Date
Order by t.title, t.Date, y.Date, m.Date
Results:
Title t.Date y.Date m.Date y.Amount m.Amount
----- ---------- ---------- ---------- -------- --------
Cust A 2021-01-01 2021-01-01 2021-01-01 2 2
Cust A 2021-01-05 2021-01-01 2021-01-01 2 2
Cust A 2021-01-05 2021-01-01 2021-01-05 2 3
Cust A 2021-01-05 2021-01-05 2021-01-01 3 2
Cust A 2021-01-05 2021-01-05 2021-01-05 3 3
Cust A 2021-02-01 2021-01-01 2021-02-01 2 5
Cust A 2021-02-01 2021-01-05 2021-02-01 3 5
Cust A 2021-02-01 2021-02-01 2021-02-01 5 5
Here is a modified select that produces correct results:
Select a.title, a.Date,
Sum(Case When datediff(year, b.Date, a.Date) = 0 Then b.Amount Else 0 End) YTD,
Sum(Case When datediff(month, b.Date, a.Date) = 0 Then b.Amount Else 0 End) MTD
From [table] a
join [table] b
on a.Title = b.Title
and b.Date <= a.Date
Group by a.title, a.Date
Results:
Title Date YTD MTD
------ ---------- ----- -----
Cust A 2021-01-01 2.00 2.00
Cust A 2021-01-05 5.00 5.00
Cust A 2021-02-01 10.00 5.00
Here is a SQL Fiddle with all the current answers plus new solutions.
SELECT ID,
Title,
Date,
Amount,
MTD = SUM(Amount) OVER (PARTITION BY Title, DATEADD(MONTH, DATEDIFF(MONTH, 0, [Date]), 0)),
YTD = SUM(Amount) OVER (PARTITION BY Title, DATEADD(YEAR, DATEDIFF(YEAR, 0, [Date]), 0))
FROM [a_table]
Another solution by using Cross Apply:
see this sqlfiddle
CREATE TABLE [table] (
ID int IDENTITY(1,1) PRIMARY KEY,
Title nvarchar(20),
Date date,
Amount money
)
INSERT INTO [table] (Title, Date, Amount)
VALUES ('Cust A', '2021-01-01', 2.00),
('Cust A', '2021-01-05', 3.00),
('Cust A', '2021-02-05', 4.00),
('Cust A', '2021-02-06', 5.00),
('Cust B', '2021-01-01', 20.00),
('Cust B', '2021-01-05', 30.00),
('Cust B', '2021-02-05', 40.00),
('Cust B', '2021-02-06', 50.00);
SELECT t1.title
,t1.Date
,t1.Amount
,t2.Amount_YTD
,t3.Amount_MTD
FROM [table] t1
CROSS APPLY
(
SELECT SUM(Amount) AS Amount_YTD
FROM [table] t2
WHERE t2.Date <= t1.Date
AND t1.Title = t2.Title
AND datediff(year, t1.Date, t2.Date) = 0
) t2
CROSS APPLY
(
SELECT SUM(Amount) AS Amount_MTD
FROM [table] t3
WHERE t3.Date <= t1.Date
AND t1.Title = t3.Title
AND datediff(month, t1.Date, t3.Date) = 0
) t3

Statement of account from transactions SQL

I have a list of transactions in a table for a user which records, the UserID, DateTime, TransactionType and Value. TransactionID is the Primary Key auto increment 1. The TransactionType defines a Deposit (1) or Withdrawal (2) so all values are positive in the table. I am trying to create a statement of account with a running total.
TransactionID UserID DateTime TransactionTypeID Value
1 3112 01-04-2016 12:00 1 5.00
3 3112 01-04-2016 13:00 2 2.00
5 3112 01-04-2016 13:25 2 1.00
8 3112 02-04-2016 12:00 1 10.00
9 3112 02-04-2016 12:35 2 4.00
Basically I want to create a running statement of account query with a Total value to create:
DateTime TransactionTypeID Deposit Withdrawal Balance
01-04-2016 12:00 1 5.00 5.00
01-04-2016 13:00 2 2.00 3.00
01-04-2016 13:25 2 1.00 2.00
02-04-2016 12:00 1 10.00 12.00
02-04-2016 12:35 2 4.00 8.00
I have tried using OUTER APPLY to select the previous transaction but with no prevail in a single query. Any assistance would be appreciated
SELECT
[UserID], [DateTime],
T.[Value] *
(CASE
WHEN [TransactionTypeID] IN (1, -- deposit
2 -- withdrawal
)
THEN -1
ELSE 1
END),
T2.Value AS PrevValue
FROM
[Transaction] T
OUTER APPLY
(SELECT TOP 1 T2.[Value]
FROM [Transaction] T2
WHERE UserID = 3112
AND T2.[TransactionID] > T.TransactionID
ORDER BY T2.TransactionID) AS T2
WHERE
T.[UserID] = 3112
ORDER BY
T.[TransactionID] DESC
the query you're asking for should look something like this..
SQL 2005
SELECT [DateTime],
[TransactionID],
[Deposit] = CASE WHEN TransactionTypeID = 1 THEN [Value] END,
[Withdrawal] = CASE WHEN TransactionTypeID = 2 THEN -[Value] END,
[Balance] = CASE TransactionTypeID WHEN 1 THEN [Value]
WHEN 2 THEN -[Value] END + ISNULL([PrevValue], 0)
FROM [Transaction] t1
OUTER APPLY (SELECT SUM(CASE TransactionTypeID WHEN 1 THEN [Value]
WHEN 2 THEN -[Value] END) AS [PrevValue]
FROM [Transaction] t2
WHERE t1.UserID = t2.UserID AND t2.[DateTime] < t1.[DateTime]) ca
WHERE UserID = 3112
SQL 2008+
SELECT [DateTime],
[TransactionID],
[Deposit] = CASE WHEN TransactionTypeID = 1 THEN [Value] END,
[Withdrawal] = CASE WHEN TransactionTypeID = 2 THEN -[Value] END,
[Balance] = SUM(CASE TransactionTypeID WHEN 1 THEN [Value]
WHEN 2 THEN -[Value] END)
OVER (ORDER BY [DateTime])
FROM [Transaction]
WHERE UserID = 3112
I Have found a SQL Server 2012 solution. If you are running an older version please check alternatives.
It uses SUM OVER..
select [TransactionID],
[DateTime],
( CASE WHEN [TransactionTypeID] IN (
1, -- deposits
2 -- withdrawals
)
THEN -[Value]
ELSE [Value]
END
),
sum(( CASE WHEN [TransactionTypeID] IN (
1, -- deposits
2 -- withdrawals
)
THEN -[Value]
ELSE [Value]
END
)) over(order by [DateTime] rows unbounded preceding) as runningtotal
from [Transaction]
WHERE UserID = 3112