Sum over N days in SQL server - sql

I have below table
AccountID
Date
Amount
123
07/06/2021
2000
123
07/12/2021
9000
123
07/16/2021
500
123
07/20/2021
500
123
07/28/2021
500
I am trying to sum the amount over 5 working days and get the output like below
AccountID
Date
Sum Amount
123
07/06/2021
11000
123
07/12/2021
9500
123
07/16/2021
1000
123
07/20/2021
500
123
07/28/2021
500
Also I am trying to ignore weekends(Saturday and Sunday)
I was able to add over 5 days using the below query. But not able to skip weekends.
Select distinct
t1.accountid,
convert(datetime,t1.[date]),
t1.amount,
sum(t2.amount)
from [dbo].[HANMI_ABRIGO_TRANSACTIONS] t1
cross apply
(
SELECT *
FROM [dbo].[HANMI_ABRIGO_TRANSACTIONS] a
WHERE a.accountid= t1.accountid
AND
(
convert(datetime,a.[date]) < DATEADD(DAY,5,convert(datetime,t1.[date]))
AND
convert(datetime,a.[date]) >= convert(datetime,t1.[date])
)
And a.accountid = '123'
And a.date like '2021-07%'
and a.amount > 0
)t2
where t1.accountid = '123'
And t1.date like '2021-07%'
and t1.amount > 0
group by
t1.accountid,
convert(datetime,t1.[date]),
t1.amount
order by convert(datetime,t1.[date])
Thanks!

I think this is the query you are asking for:
SELECT AccountId, Date,
(
SELECT SUM(Amount)
FROM HANMI_ABRIGO_TRANSACTIONS h2
WHERE
h1.AccountID = h2.AccountID and
DATEPART(WEEKDAY, h2.Date) not in (1, 7) and
h2.Date between h1.Date AND DATEADD(d, 5, h1.Date)
) as SumAmount
FROM HANMI_ABRIGO_TRANSACTIONS h1
The results are:
AccountId
Date
SumAmount
123
2021-07-06
2000
123
2021-07-12
9500
123
2021-07-16
1000
123
2021-07-20
500
123
2021-07-28
500
SQL Fiddle: http://sqlfiddle.com/#!18/3d6bae/8

Related

Performing aggregate function on a date range

How to perform an aggregate function (AVG) on a date range
Source data looks like:
AccNo Amt Date
1 100 2/1/2021
1 200 2/5/2021
1 300 3/3/2021
2 400 5/20/2021
2 500 5/18/2021
The target needs to be derived in the following method:
Ex: To derive avg_past_week calculate the average amount for all the rows that are within the date range Date to Date - 7
Similarily, for avg_past_month, it'll be Date to Date - 30
Target:
AccNo Amt Date Avg_past_week Avg_past_month Avg_past_3 month
1 100 2/1/2021 100 100 100
1 200 2/5/2021 150 150 150
1 300 3/3/2021 300 250 200
2 400 5/20/2021 450 450 450
2 500 5/18/2021 500 500 500
select a.AccNo, a.Amt, a.Date,
avg(b.Amt) as Amt7,
avg(c.Amt) as Amt30,
avg(d.Amt) as Amt90
from T a
join T b on a.AccNo = b.AccNo and b.Date > DateAdd(day, -7, a.Date) and b.date <= a.Date
join T c on a.AccNo = c.AccNo and c.Date > DateAdd(day, -30, a.Date) and c.date <= a.Date
join T d on a.AccNo = d.AccNo and d.Date > DateAdd(day, -90, a.Date) and d.date <= a.Date
group by a.AccNo, a.Amt, a.Date
result
1 100 2021-02-01 100 100 100
1 200 2021-02-05 150 150 150
1 300 2021-03-03 300 250 200
2 400 2021-05-20 450 450 450
2 500 2021-05-18 500 500 500
You can use window functions:
select t.*,
avg(amt) over (partition by accNo
order by datediff('2000-01-01', date)
range between 6 preceding and current row
) as avg_7day,
avg(amt) over (partition by accNo
order by datediff('2000-01-01', date)
range between 30 preceding and current row
) as avg_30day,
avg(amt) over (partition by accNo
order by datediff('2000-01-01', date)
range between 90 preceding and current row
) as avg_91day
from t;
This converts the date to a number (number of days since some arbitrary date) and then uses a window frame for the time period.
Note that the number of days going back is one less than the period, because the current day is included as well.

Closing balance of the previous day as an Opening balance of today

I am developing a database application for a small electronics business. I need a SQL query which takes the closing balance of previous day as an opening balance of current day. I have following data tables
Expensis
ExpenseID Date Expense
1 2019-03-01 2,000
2 2019-03-02 1,000
3 2019-03-03 500
Income
IncomeID Date Income
1 2019-03-01 10,000
2 2019-03-02 13,000
3 2019-03-03 10,000
Required result
Date Opening Balance Income Expense Closing Balance
2019-03-01 0 10,000 2,000 8,000
2019-03-02 8,000 13,000 1,000 20,000
2019-03-03 20,000 10,000 5,00 29,500
You can use sum aggregation function recursively ( lag window analytic function cannot be used for sql server 2008 )
with Expensis( ExpenseID, Date, Expense ) as
(
select 1, '2019-03-01', 2000 union all
select 2, '2019-03-02', 1000 union all
select 3, '2019-03-03', 500
), Income( IncomeID, Date, Income ) as
(
select 1, '2019-03-01', 10000 union all
select 2, '2019-03-02', 13000 union all
select 3, '2019-03-03', 10000
), t as
(
select i.date,
i.income,
e.expense,
sum(i.income-e.expense) over (order by i.date) as closing_balance
from income i
join expensis e on e.date = i.date
)
select date,
( closing_balance - income + expense ) as opening_balance,
income, expense, closing_balance
from t;
date opening balance income expense closing balance
---------- --------------- ------ ------- ---------------
2019-03-01 0 10000 2000 8000
2019-03-02 8000 13000 1000 20000
2019-03-03 20000 10000 500 29500
Demo
Here is one way you could do it
You have to valuate income and expenses differently
WITH INCOME AS
(
SELECT '2018-01-05' AS DT, 200 AS INC, 1 AS TP
UNION ALL
SELECT '2018-01-06' AS DT, 300 AS INC, 1 AS TP
UNION ALL
SELECT '2018-01-07' AS DT, 400 AS INC, 1 AS TP
)
, EXPENSES AS
(
SELECT '2018-01-05' AS DT, -100 AS EXPS, 2 AS TP
UNION ALL
SELECT '2018-01-06' AS DT, -500 AS EXPS, 2 AS TP
UNION ALL
SELECT '2018-01-07' AS DT, -30 AS EXPS, 2 AS TP
)
, UN AS
(
SELECT * FROM INCOME
UNION ALL
SELECT * FROM EXPENSES
)
SELECT *, [1]+[2] AS END_BALANCE FROM UN
PIVOT
(
SUM(INC)
FOR TP IN ([1],[2])
) AS P

Incremental sales Output

I have a table like Bikes
name mfgdate qtysold
bajaj 1/1/2016 12:00:00 AM 48
bajaj 1/1/2017 12:00:00 AM 49
bajaj 1/1/2018 12:00:00 AM 50
pulsar 1/1/2016 12:00:00 AM 300
pulsar 1/1/2017 12:00:00 AM 250
pulsar 1/1/2018 12:00:00 AM 200
yamaha 1/1/2016 12:00:00 AM 90
yamaha 1/1/2017 12:00:00 AM 180
yamaha 1/1/2018 12:00:00 AM 100
From the above table i need a select Query to get the output of the increased sales
like
eg: output
name
Bajaj
because Bajaj has the increased sales. Yamaha also have a increased sales but it decreased in 2018..
Thanks in advance..
Based on OP's comments, it is required to find only those Bike names, which has never seen decrease in sales.
Following solution will work on All MySQL Versions (especially < 8.0)
Using a Correlated subquery, we find previous qtysold value for a bike. If not found, using Ifnull() function, consider it as zero
Calculate change (qtysold - previous qtysold)
Now, using this as Derived table, we can get all those Distinct bike name(s), where change has always been > 0
You can try the following (check SQL Fiddle):
SELECT t3.name
FROM
(
SELECT t1.name,
(t1.qtysold - IFNULL(
(SELECT t2.qtysold
FROM Bikes as t2
WHERE t2.name = t1.name
AND t2.mfgdate < t1.mfgdate
ORDER BY t2.mfgdate DESC LIMIT 1)
, 0
)) AS sales_change
FROM Bikes as t1
) AS t3
GROUP BY t3.name
HAVING MIN(t3.sales_change) > 0
You can use a SELF JOIN to get desired result. Inner query will return the names of all Bikes which have a dip in sales. You can use NOT IN clause then to find the left-out result entries.
SELECT DISTINCT b.name
FROM Bikes b
WHERE
b.name NOT IN (
SELECT DISTINCT b1.name
FROM
Bikes b1
INNER JOIN
Bikes b2
ON b1.name = b2.name
AND b1.mfgdate < b2.mfgdate
AND b1.qtysold > b2.qtysold
);
You can use window function :
select t.*,
sum(case when exists (select 1
from table t1
where t1.name = t.name and t1.mfgdate > t.mfgdate and t1.qtysold < t.qtysold
)
then 1 else 0 end
) over (partition by name) as grp
from table t;
Now you can filter out the names which has increaed sales :
select t.*
from ( <subquery here> ) t
where t.grp = 0;
MySQL Self Join:
SELECT
*
FROM
Bikes AS this_year
LEFT JOIN
Bikes AS prev_year
ON prev_year.name = this_year.name
AND prev_year.mfgdate = DATE_SUB(this_year.mfgdate, INTERVAL 1 YEAR)
GROUP BY
this_year.name
HAVING
MIN(this_year.qtysold - COALESCE(prev_year.qtysold, 0)) > 0
MariaDB LAG():
WITH
historicised AS
(
SELECT
*,
LAG(qtysold) OVER (PARTITION BY name
ORDER BY mfgdate
)
AS prev_qtysold
FROM
Bikes
)
SELECT
name
FROM
historicised
GROUP BY
name
HAVING
MIN(qtysold - COALESCE(prev_qtysold, 0)) > 0

How can I select all records and the match of both tables get in one record

I want to get all the records from two tables, calculating the sum of the amount grouped by day, but showing all the records from both, when the date match show both values.
For example I have:
table 1 In
id ammount date
1 300 2017-10-25
2 150 2017-10-25
3 550 2017-10-27
table 2 out
1 250 2017-10-27
2 410 2017-10-28
3 830 2017-10-29
and I want this result:
result
ammount in ammount out date
450 0 2017-10-25
550 250 2017-10-27
0 410 2017-10-28
0 830 2017-10-29
any idea how to make this?
This works with SQLite3:
create table t1(id,amount,date);
insert into t1 values
(1,300,'2017-10-25'),
(2,150,'2017-10-25'),
(3,550,'2017-10-27');
create table t2(id,amount,date);
insert into t2 values
(1,250,'2017-10-27'),
(2,410,'2017-10-28'),
(3,830,'2017-10-29');
select sum(amount_in) as amount_in, sum(amount_out) as amount_out, date
from (
select amount as amount_in, 0 as amount_out, date from t1
union all
select 0 as amount_in, amount as amount_out, date from t2
)
group by date;
That's a full outer join on the two aggregated tables:
select
coalesce(tin.ammount, 0) as ammount_in,
coalesce(tout.ammount, 0) as ammount_out,
date
from (select date, sum(ammount) as total from table_in group by date) tin
full outer join (select date, sum(ammount) as total from table_out group by date) tout
using (date);
This is works in MS sql server
SELECT SUM(amount_in) AS amount_in, SUM(amount_out) AS amount_out, date1
FROM (SELECT amount AS amount_in, 0 AS amount_out, date1
FROM t1
UNION ALL
SELECT 0 AS amount_in, amount AS amount_out, date1
FROM t2) AS derivedtbl_1
GROUP BY date1

return enddate where cumulative sum of fee column less than 1100 using sql

end date fee
-----------------
05-Sep-14 700
12-Sep-14 200
19-Sep-14 100
26-Sep-14 300
03-Oct-14 400
In the table shown here, I need to return enddate where cumulative sum of fee column is less than 1100 using SQL.
Example:
19-Sep-14 (700 + 200 + 100 < 1100)
SELECT TOP 1
t1.enddate,
t1.fee,
SUM(t2.fee) as cumulative_sum
FROM test t1
INNER JOIN tableName t2 on t1.enddate >= t2.enddate
GROUP BY t1.enddate, t1.fee
HAVING SUM(t2.fee) < 1100
ORDER BY t1.enddate DESC
Data sample
create view fees
as
select cast('05-Sep-14' as date) as end_date, 700 as fee
union all select '12-Sep-14', 200
union all select '19-Sep-14', 100
union all select '26-Sep-14', 300
union all select '03-Oct-14', 400
Solution
SELECT TOP 1
a.end_date,
SUM(b.fee) as cumulative
FROM fees a CROSS JOIN fees b
WHERE a.end_date >= b.end_date
GROUP BY a.end_date
HAVING SUM(b.fee) < 1100
ORDER BY end_date desc