Get last two entries of each account in table - sql

I've got script that gives me all transactions for day for all accounts and sub accounts. His return you can see on the image. What I want, is return result as two last transactions for each accountId and subaccountId. Ideal return would be:
AccountId| SubAccountId| AmountInDay | Date
---------------------------------------------
210 | 1 | 0.00 |2017-06-20 00:00:00.000
210 | 1 | 0.00 |2017-06-05 00:00:00.000
1234 | 1 | 0.00 |2017-06-20 00:00:00.000
1234 | 1 | 0.00 |2017-06-05 00:00:00.000
This is the code of my script:
with CTE1 as
(
select top 2 AccountId, SubAccountId, [Date], sum(Amount_Amount) as Amount
from dbo.PayoutInstallment
group by accountId, SubAccountId, [Date]
)
, CTE2 as
(
select AccountId,SubAccountId, Amount_Amount, [Date],
dense_rank() over (partition by AccountId order by [Date] desc) as rn
from dbo.PayoutInstallment
)
select a1.AccountId,a1.SubAccountId, Sum(a1.Amount_Amount) as AmountInDay, a1.[Date]
from CTE2 a1
left join CTE2 a2
on a1.AccountId = a2.AccountId and a1.[Date] > a2.[Date]
and a2.rn = a1.rn+1
group by a1.[Date], a1.AccountId, a1.SubAccountId
order by a1.[Date] desc
EDIT
Sample Data
AccountId| SubAccountId| AmountInDay | Date
---------------------------------------------
210 | 1 | 0.00 |2017-03-15 00:00:00.000
210 | 1 | 0.00 |2017-04-20 00:00:00.000
210 | 1 | 100.00 |2017-05-17 00:00:00.000
210 | 1 | 1.00 |2017-06-05 00:00:00.000
210 | 1 | 1.00 |2017-06-05 00:00:00.000
1234 | 1 | 0.00 |2017-06-05 00:00:00.000
1234 | 1 | 0.00 |2017-06-05 00:00:00.000
1234 | 1 | 1.00 |2017-06-10 00:00:00.000
1234 | 1 | 1.00 |2017-04-10 00:00:00.000

I think you can use row_number and get 2 records as below:
Select * from (
Select AccountId, SubAccountId, [Date], sum(Amount_Amount) over (partition by accountid, SubAccountId, [Date])
,RowN = Row_number() over (partition by accountid, SubAccountId, [Date] order by [date] desc)
from dbo.PayoutInstallment
) a where a.RowN <= 2

Assume one day one transaction,
;WITH cte AS(SELECT *
, ROW_NUMBER() OVER (PARTITION BY AccountId, SubAccountId ORDER BY [Date] DESC) AS Rownum
FROM PayoutInstallment
)
SELECT *
, SUM(AmountInDay) OVER (PARTITION BY AccountId, SubAccountId) AS SumLast2days
FROM cte
WHERE Rownum<=2

If you want the SUM for the last two day you need to assign a number to each day. Then bring all the data related to those days by JOIN both dataset and then perform a GROUP BY
WITH cte as (
SELECT AccountId, SubAccountId, [Date],
ROW_NUMBER() OVER (PARTITION BY AccountId, SubAccountId
ORDER BY [Date] DESC) AS rn
FROM dbo.PayoutInstallment
)
SELECT P.AccountId,
P.SubAccountId,
P.[Date],
SUM(ammount)
FROM dbo.PayoutInstallment P
JOIN cte C
ON P.[Date] = C.[Date]
AND P.AccountId = C.AccountId
AND P.SubAccountId = C.SubAccountId
WHERE rn <= 2 -- Just the last day of each account, subacount
GROUP BY P.AccountId,
P.SubAccountId,
P.[Date]

I see you are using GROUP BY, so if you want the results to be sorted after the grouping, you should use HAVING if you want otherwise you should use WHERE. Here is an example of a WHERE clause you can use in your query to get only results between the last two days.
WHERE (a1.[Date] BETWEEN GETDATE()AND GETDATE()-2)

Related

Add first and last date of a sequence

I am working on a database which have a huge collection of rows. I want to update it so repeated records will be deleted. Now, I have a date column in table and I want to convert it into startDate and endDate. Please check:
id | date | price | minutes | prefixId | sellerId | routeTypeId
1234 2020-01-01 0.123 0 1 1 1
1235 2020-01-04 0.123 0 1 1 1
1236 2020-01-05 0.123 123 1 1 1
1237 2020-01-06 0.123 31 1 1 1
1238 2020-01-07 0.123 23 1 1 1
1239 2020-01-08 0.130 41 1 2 1
1240 2020-01-09 0.130 0 1 1 1
What I am looking for is:
id | startDate | endDate | price | minutes | prefixId | sellerId | routeTypeId
1234 2020-01-01 2020-01-01 0.123 0 1 1 1
1235 2020-01-04 2020-01-07 0.123 0 1 1 1
1239 2020-01-08 2020-01-08 0.130 41 1 2 1
1240 2020-01-09 2020-01-09 0.130 0 1 2 2
Dates will be considered in a series if price, prefixId, sellerId, routeTypeId will remain same with previous row and date column generates a series (without any gap between dates. So, 2020-01-01, 2020-01-2, 2020-01-10 are two different series for example)
This is a gaps-and-islands problem. You can use lag() and a cumulative sum:
select price, prefixId, sellerId, routeTypeId,
min(minutes),
min(date), max(date)
from (select t.*,
sum(case when prev_date = date - interval '1 day' then 0 else 1 end) over (order by date) as grp
from (select t.*,
lag(date) over (partition by price, prefixId, sellerId, routeTypeId order by date) as prev_date
from t
) t
) t
group by grp, price, prefixId, sellerId, routeTypeId
This is a "Gaps & Islands" problem. You can do it using:
select
min(id) as id,
min(date) as start_date,
max(date) as end_date,
min(price) as price,
...
from (
select *,
sum(inc) over(order by id) as grp
from (
select *,
case when price = lag(price) over(order by id)
and date = lag(date) over(
partition by price, prefixId, sellerId, routeTypeId
order by id)
+ interval '1 day'
then 0 else 1 end as inc
from t
) x
) y
group by grp

How to get daily budget based on monthly budget and workings days

Have have 2 tables.
One table with month budget, and one table with workings days.
What I want, is find out daily budget based on the monthly budget and working days.
Example:
August have a budget on 1000 and have 21 workings day.
September have a budget on 2000 and 23 workings days
I want to figure out what the total budget betweens two dates.
Ex: between 2020-08-02 and 2020-09-15
But must be sure that, days in august takes budget from august, days from september takes budget from september etc.
tbBudget:
Date | Amount
2020-08-01 | 1000
2020-09-01 | 2000
2020-10-01 | 3000
tbWorkingDays
Date | WorkingDay
2020-08-01 | 0
2020-08-02 | 0
2020-08-03 | 1
2020-08-04 | 1
2020-08-05 | 1
2020-08-06 | 1
2020-08-07 | 1
2020-08-08 | 1
...
2020-09-01 | 1
2020-09-02 | 1
2020-09-03 | 0
2020-09-04 | 1
...
2020-10-01 | 1
2020-10-02 | 0
2020-10-03 | 1
2020-10-04 | 1
I have no idea how to solve this issue. Can you help me?
My result should be like:
Date | WorkingDay | BudgetAmount
2020-08-02 | 0 | 0.0
2020-08-03 | 1 | 47.6
2020-08-04 | 1 | 47.6
2020-08-05 | 1 | 47.6
..
2020-09-13 | 1 | 86.9
2020-09-14 | 1 | 86.9
2020-09-15 | 1 | 86.9
Using CTE and group by:
with CTE1 AS(
SELECT FORMAT(A.DATE, 'MMyyyy') DATE, B.AMOUNT, SUM(CASE WHEN [WorkingDay] = 1 THEN 1 ELSE 0 END) AS TOTAL_WORKING_DAYS
FROM tbWorkingDays A INNER JOIN tbBudget B
ON (FORMAT(A.DATE, 'MMyyyy') = FORMAT(B.DATE, 'MMyyyy')) GROUP BY FORMAT(A.[DATE], 'MMyyyy'), B.AMOUNT
)
SELECT A.DATE,
A.WORKINGDAY,
CASE WHEN A.WORKINGDAY = 1 THEN B.AMOUNT/B.TOTAL_WORKING_DAYS
ELSE 0 END AS BudgetAmount
FROM CTE1 B
INNER JOIN
tbWorkingDays A
ON (FORMAT(A.DATE, 'MMyyyy') = B.DATE);
Assuming that the budgets are by month:
select wd.*,
(case when workingday = 0 then 0
else wd.budget * 1.0 / sum(wd.workingday) over (partition by wd.date)
end) as daily_amount
from tbWorkingDays wd join
tblBudget b
on wd.date >= b.date and wd.date < dateadd(month, 1, wd.date);
If the budget dates are not per month, then use apply instead:
select wd.*,
(case when workingday = 0 then 0
else wd.budget * 1.0 / sum(wd.workingday) over (partition by wd.date)
end) as daily_amount
from tbWorkingDays wd cross apply
(select top (1) b.*
from tblBudget b
where wd.date >= b.date
order by b.date desc
) b
Use sum as an analytical function to get the number of workingdays pr month, then divide out
Here is a functioning solution
with tally as
(
SELECT
row_number() over (order by (select null))-1 n
from (values (null),(null),(null),(null),(null),(null),(null),(null),(null),(null),(null)) a(a)
cross join (values (null),(null),(null),(null),(null),(null),(null),(null),(null),(null),(null)) b(b)
cross join (values (null),(null),(null),(null),(null),(null),(null),(null),(null),(null),(null)) c(c)
)
, tbWorkingDays as
(
select
cast(dateadd(day,n,'2020-01-01') as date) [Date],
iif(DATEPART(WEEKDAY,cast(dateadd(day,n,'2020-01-01') as date)) in (1,7),0,1) WorkingDay
from tally
where n<365
)
, tbBudget AS
(
select * from
(values
(cast('2020-08-01' as date), cast(1000 as decimal(19,2)))
,(cast('2020-09-01' as date), cast(2000as decimal(19,2)))
,(cast('2020-10-01' as date), cast(3000as decimal(19,2)))
) a([Date],[Amount])
)
select
a.[Date]
,a.WorkingDay*
(b.Amount/
sum(a.WorkingDay) over (partition by year(a.Date)*100+month(a.Date)))
from tbWorkingDays a
inner join tbBudget b
on a.Date between b.Date and dateadd(day,-1,dateadd(month,1,b.date))
The work is done here:
select
a.[Date]
,a.WorkingDay*
(b.Amount/
sum(a.WorkingDay) over (partition by year(a.Date)*100+month(a.Date)))
from tbWorkingDays a
inner join tbBudget b
on a.Date between b.Date and dateadd(day,-1,dateadd(month,1,b.date))
The expression
sum(a.WorkingDay) over (partition by year(a.Date)*100+month(a.Date))
Sums the number of workingdays for the current month. I then join against the budget and take the sum for the month and divide by the expression above.
To make sure there only is budget on workingdays, I simply multiply by "workingday", since 0 is a non workingday, the sum will be 0 for all non workingdays.

Pivot DateTime fields by date

I have a table that contains employee 'punches' (clock ins/outs) each punch can be an 'in'(punch_type=1) or an 'out' (punch_type=2).
The table is formatted as follows:
emp_num | report_date | punch_time | punch_type
-----------------------------------------------------------
1 | 2018-04-20 |2018-04-20 04:46:00.000 | 1
1 | 2018-04-20 |2018-04-20 06:58:00.000 | 2
1 | 2018-04-20 |2018-04-20 08:10:00.000 | 1
1 | 2018-04-20 |2018-04-20 12:00:00.000 | 2
I am trying to get the first 'punch' (clock in) and the following 'punch' (clock out) in the same row. Then, of course, any following would be the same.
Desired output:
emp_num | report_date | punch_in | punch_out
-----------------------------------------------------------
1 | 2018-04-20 |2018-04-20 04:46:00.000 | 2018-04-20 06:58:00.000
1 | 2018-04-20 |2018-04-20 08:10:00.000 | 2018-04-20 12:00:00.000
Keep in mind there may be multiple punch in/out combos in one day as shown in the example.
Any help would be greatly appreciated!
First you want to know which punch out time belongs to which punch in time. Answer: the nth punch out time belongs to the nth punch in time. So number your records:
select
p_in.emp_num,
p_in.report_date,
p_in.punch_time as punch_in,
p_out.punch_time as punch_out
from
(
select
emp_num,
report_date,
punch_time,
row_number() over (partition by emp_num, report_date order by punch_time) as rn
from mytable
where punch_type = 1
) p_in
left join
(
select
emp_num,
report_date,
punch_time,
row_number() over (partition by emp_num, report_date order by punch_time) as rn
from mytable
where punch_type = 2
) p_out on p_out.emp_num = p_in.emp_num
and p_out.report_date = p_in.report_date
and p_out.rn = p_in.rn
order by p_in.emp_num, p_in.report_date, punch_in;
select emp_num, report_date, max(case when punch_type=1 then punch_time else null end) punch_in,
max(case when punch_type=2 then punch_time else null end) punch_out
from (select *, row_number() over(partition by emp_num, report_date, punch_type order by emp_num, report_date, punch_time) value from yourtable )a
group by emp_num, report_date, value

Running Total on date column

I have the following data in my table:
id invoice_id date ammount
1 1 2012-01-01 100.00
20 1 2012-01-31 50.00
470 1 2012-01-15 300.00
Now, I need to calculate running total for an invoice in some period. So, the output for this data sample should look like this:
id invoice_id date ammount running_total
1 1 2012-01-01 100.00 100.00
470 1 2012-01-15 300.00 400.00
20 1 2012-01-31 50.00 450.00
I tried with this samples http://www.sqlusa.com/bestpractices/runningtotal/ and several others, but the problem is that I could have entries like id 20, date 2012-01-31 and id 120, date 2012-01-01, and then I couldn't use NO = ROW_NUMBER(over by date)... in first select and then ID < NO in second select for calculating running total.
DECLARE #DateStart DATE='2012-01-01';
WITH cte
AS (SELECT id = Row_number() OVER(ORDER BY [date]),
DATE,
myid = id,
invoice_id,
orderdate = CONVERT(DATE, DATE),
ammount
FROM [Table_2]
WHERE DATE >= #DateStart)
SELECT myid,
invoice_id,
DATE,
ammount,
runningtotal = (SELECT SUM(ammount)
FROM cte
WHERE id <= a.id)
FROM cte AS a
ORDER BY id

How to determine the maximum value for each category in SQL?

My table has records like below:
ID EmpID EffectiveDate PayElement Amount ComputeType AddDeduction
42 ISIPL001 2010-04-16 00:00:00.000 Basic 8000.00 On Attendance Addition
43 ISIPL001 2010-04-01 00:00:00.000 Con 2000.00 On Attendance Addition
44 ISIPL001 2010-04-01 00:00:00.000 HRA 2000.00 On Attendance Addition
54 ISIPL001 2011-01-01 00:00:00.000 Basic 15000.00 On Attendance Addition
55 ISIPL001 2011-01-01 00:00:00.000 Con 6000.00 On Attendance Addition
57 ISIPL001 2011-01-01 00:00:00.000 HRA 6000.00 On Attendance Addition
61 ISIPL001 2010-07-10 00:00:00.000 Basic 12000.00 On Attendance Addition
66 ISIPL001 2010-07-10 00:00:00.000 HRA 4200.00 On Attendance Addition
68 ISIPL001 2010-07-10 00:00:00.000 Con 5600.00 On Attendance Addition
I want the result display below:
i.e for each pay element available in my database, I need to record which is having maximum date for each pay element.
So my output should be like given below:
54 Basic 15000
55 Con 6000
57 HRA 6000
Try this:
SELECT ID,
PayElement,
Amount
FROM (
SELECT a.*,
RANK() OVER(PARTITION BY PayElement ORDER BY EffectiveDate DESC) AS rn
FROM <YOUR_TABLE> a
) a
WHERE rn = 1
;with cte as
(
select *,
row_number() over(partition by PayElement order by EffectiveDate desc) as rn
from YourTable
)
select
ID,
PayElement,
Amount
from cte
where rn = 1
Try this.
select
T.ID,
T.PayElement,
T.Amount
from
Test T inner join (select MAX(T_DATE.EffectiveDate) as MAX_DATE, T_DATE.PayElement from Test T_DATE group by T_DATE.PayElement) T_DATE on (T.PayElement = T_DATE.PayElement) and (T.EffectiveDate = T_DATE.MAX_DATE)
order by
T.ID
Select a.Id,
a.PayElement,
a.Amount
From dbo.YourTable a
Join
(
Select PayElement,
Max(EffectiveDate) as[MaxDate]
From dbo.YourTable
Group By PayElement
)b on a.PayElement = b.PayElement
And a.EffectiveDate = b.MaxDate
try something like
Select
a.ID, a.PayElement, a.Amount
From MyTable a
Inner Join (
Select PayElement, max(EffectiveDate) as MaxDate From MyTable Group By PayElement
) sub on a.EffectiveDate = sub.MaxDate and a.PayElement = sub.PayElement
select
Id, PayElement, Amount
from
YourTable a
inner join
(select
Id, PayElement, max(EffectiveDate) as EffectiveDate
from
YourTable
group by
PayElement, Id) b
on
a.Id = b.Id