Need YTD and MTD calculations in SQL - sql

Date Amt ytd mtd
01-Jan-21 1 2 2
01-Jan-21 1 2 2
02-Jan-21 1 3 3
03-Jan-21 1 4 4
01-Feb-21 1 5 1
02-Feb-21 1 6 2
03-Feb-21 1 7 3
04-Feb-21 1 8 4
05-Feb-21 1 9 5
01-Mar-21 1 10 1
02-Mar-21 1 11 2
03-Mar-21 1 12 3
04-Mar-21 1 13 4
01-Apr-21 1 14 1
02-Apr-21 1 15 2
03-Apr-21 1 16 3
01-May-21 1 17 1
02-May-21 1 18 2
03-May-21 1 19 3
04-May-21 1 20 4
05-May-21 1 21 5
06-May-21 1 22 6
I have the first two columns (Date, Amt) and i need the YTD and MTD columns in MS SQL so that i can show the above table.

Seems like a rolling COUNT OVER was used to calculate the ytd & mtd in the Oracle source.
(Personally, I would prefere RANK or DENSE_RANK)
And since Oracle datestamps can be casted to a DATE as-is.
SELECT [Date], Amt
, ytd = COUNT(*) OVER (ORDER BY CAST([Date] AS DATE))
, mtd = COUNT(*) OVER (PARTITION BY EOMONTH(CAST([Date] AS DATE)) ORDER BY CAST([Date] AS DATE))
FROM your_table
ORDER BY CAST([Date] AS DATE)
Date
Amt
ytd
mtd
01-Jan-21
1
2
2
01-Jan-21
1
2
2
02-Jan-21
1
3
3
03-Jan-21
1
4
4
01-Feb-21
1
5
1
02-Feb-21
1
6
2
03-Feb-21
1
7
3
04-Feb-21
1
8
4
05-Feb-21
1
9
5
db<>fiddle here

Related

Joining two tables where date in second table should be first date after date in first table i.e earliest date after date in first table

Problem statement description :-
I have two tables - table1 and table2 . table1 contains data of quantity buy of id=7 and table2 contain data of quantity sold of same id=7. Both table1 and table2 is sorted according to date i.e from oldest to latest date and every time only 1 quantity is buy or sold.
table1:
table1
date_buy
id
qty_buy
rolling_sum_qty_buy
30-07-2019
7
1
1
20-10-2019
7
1
2
17-01-2020
7
1
3
15-02-2020
7
1
4
15-02-2020
7
1
5
15-02-2020
7
1
6
14-07-2021
7
1
7
19-09-2021
7
1
8
25-12-2021
7
1
9
30-12-2021
7
1
10
10-02-2022
7
1
11
15-03-2022
7
1
12
15-03-2022
7
1
13
14-06-2022
7
1
14
table2:-
table2
date_sold
id
qty_sold
rolling_sum_qty_sold
01-08-2019
7
1
1
15-09-2019
7
1
2
27-12-2019
7
1
3
01-02-2020
7
1
4
12-02-2020
7
1
5
25-07-2021
7
1
6
25-07-2021
7
1
7
28-08-2021
7
1
8
10-09-2021
7
1
9
12-09-2021
7
1
10
25-04-2022
7
1
11
-- SQL scrtpt of table1:
CREATE TABLE IF NOT EXISTS table1 (
`date_buy` DATETIME,
`id` INT,
`qty_buy` INT,
`rolling_sum_qty_buy` INT
);
INSERT INTO table1 VALUES
('2019-07-30 00:00:00',7,1,1),
('2019-10-20 00:00:00',7,1,2),
('2020-01-17 00:00:00',7,1,3),
('2020-02-15 00:00:00',7,1,4),
('2020-02-15 00:00:00',7,1,5),
('2020-02-15 00:00:00',7,1,6),
('2021-07-14 00:00:00',7,1,7),
('2021-09-19 00:00:00',7,1,8),
('2021-12-25 00:00:00',7,1,9),
('2021-12-30 00:00:00',7,1,10),
('2022-02-10 00:00:00',7,1,11),
('2022-03-15 00:00:00',7,1,12),
('2022-03-15 00:00:00',7,1,13),
('2022-06-14 00:00:00',7,1,14);
-- sql script of table2:
CREATE TABLE IF NOT EXISTS table2 (
`date_sold` DATETIME,
`id` INT,
`qty_sold` INT,
`rolling_sum_qty_sold` INT
);
INSERT INTO table2 VALUES
('2019-08-01 00:00:00',7,1,1),
('2019-09-15 00:00:00',7,1,2),
('2019-12-27 00:00:00',7,1,3),
('2020-02-01 00:00:00',7,1,4),
('2020-02-12 00:00:00',7,1,5),
('2021-07-25 00:00:00',7,1,6),
('2021-07-25 00:00:00',7,1,7),
('2021-08-28 00:00:00',7,1,8),
('2021-09-10 00:00:00',7,1,9),
('2021-09-12 00:00:00',7,1,10),
('2022-04-25 00:00:00',7,1,11);
-- Now, i want to join this two table on two condition
for every date i.e date_buy column in table1 i should get output where date i.e date_sold is greater than date_buy and i want first date i.e. date_sold which is greater than that particular date i.e date_buy.
i also want those rows from table1 in my output which does not get joined with table2
so that i can easily find out the remaining quantity because in table1 i have quantity buy and after joining with table2 i will get quantity sold, so the cases where i get null values then in that case i can assume that that much quantity is remaining.
--My output:-
Earlier when there was no date issue then i was simply using left join to join table1 and table2 on rolling sum condition and where there was null cases i was taking sum of qty to get remaining qty but right now i have that condition too so i cant use rolling_sum_cond column directly in join condition.
-- query which i was using and output which i was getting earlier
select * from table1
left join table2
on table1.rolling_sum_qty_buy=table2.rolling_sum_qty_sold
date_buy
id
qty_buy
rolling_sum_qty_buy
date_sold
id-2
qty_sold
rolling_sum_qty_sold
30-07-2019
7
1
1
01-08-2019
7
1
1
20-10-2019
7
1
2
15-09-2019
7
1
2
17-01-2020
7
1
3
27-12-2019
7
1
3
15-02-2020
7
1
4
01-02-2020
7
1
4
15-02-2020
7
1
5
12-02-2020
7
1
5
15-02-2020
7
1
6
25-07-2021
7
1
6
14-07-2021
7
1
7
25-07-2021
7
1
7
19-09-2021
7
1
8
28-08-2021
7
1
8
25-12-2021
7
1
9
10-09-2021
7
1
9
30-12-2021
7
1
10
12-09-2021
7
1
10
10-02-2022
7
1
11
25-04-2022
7
1
11
15-03-2022
7
1
12
NULL
NULL
NULL
NULL
15-03-2022
7
1
13
NULL
NULL
NULL
NULL
14-06-2022
7
1
14
NULL
NULL
NULL
NULL
and to find out remaining quantity , i was using null condition
query:-
with cte as
(
select * from table1
left join table2
on table1.rolling_sum_qty_buy=table2.rolling_sum_qty_sold
)
select sum(qty_buy) as remaining_qty
from cte
where cte.date_sold is null
remaining_qty
3
-- my expectation
now i have to use date condition also to get the output
-- Expected Output
date_buy
id
qty_buy
rolling_sum_qty_buy
date_sold
id
qty_sold
rolling_sum_qty_sold
30-07-2019
7
1
1
01-08-2019
7
1
1
20-10-2019
7
1
2
27-12-2019
7
1
3
17-01-2020
7
1
3
01-02-2020
7
1
4
15-02-2020
7
1
4
25-07-2021
7
1
6
15-02-2020
7
1
5
25-07-2021
7
1
7
15-02-2020
7
1
6
28-08-2021
7
1
8
14-07-2021
7
1
7
10-09-2021
7
1
9
19-09-2021
7
1
8
25-04-2022
7
1
11
25-12-2021
7
1
9
NULL
NULL
NULL
NULL
30-12-2021
7
1
10
NULL
NULL
NULL
NULL
10-02-2022
7
1
11
NULL
NULL
NULL
NULL
15-03-2022
7
1
12
NULL
NULL
NULL
NULL
15-03-2022
7
1
13
NULL
NULL
NULL
NULL
14-06-2022
7
1
14
NULL
NULL
NULL
NULL
-- Please help me to get the following output. Any help would be appreciated.
I am using postgresql.
That was a challenging one.
with recursive cte as
(
select t2.date_sold
,t2.rolling_sum_qty_sold
,true as is_match
,1 as last_rolling_sum_qty_buy
from t2 join t on t2.rolling_sum_qty_sold = t.rolling_sum_qty_buy
where t2.rolling_sum_qty_sold = 1
union all
select t2.date_sold
,t2.rolling_sum_qty_sold
,t2.date_sold >= t.date_buy
,cte.last_rolling_sum_qty_buy + case when t2.date_sold >= t.date_buy then 1 else 0 end
from t2
join cte on cte.rolling_sum_qty_sold + 1 = t2.rolling_sum_qty_sold
join t on t.rolling_sum_qty_buy = cte.last_rolling_sum_qty_buy + 1
)
select t.date_buy
,t.id
,t.qty_buy
,t.rolling_sum_qty_buy
,cte.date_sold
,cte.rolling_sum_qty_sold
from t left join cte on cte.last_rolling_sum_qty_buy = t.rolling_sum_qty_buy and is_match
date_buy
id
qty_buy
rolling_sum_qty_buy
date_sold
rolling_sum_qty_sold
2019-07-30
7
1
1
2019-08-01
1
2019-10-20
7
1
2
2019-12-27
3
2020-01-17
7
1
3
2020-02-01
4
2020-02-15
7
1
4
2021-07-25
6
2020-02-15
7
1
5
2021-07-25
7
2020-02-15
7
1
6
2021-08-28
8
2021-07-14
7
1
7
2021-09-10
9
2021-09-19
7
1
8
2022-04-25
11
2021-12-25
7
1
9
null
null
2021-12-30
7
1
10
null
null
2022-02-10
7
1
11
null
null
2022-03-15
7
1
12
null
null
2022-03-15
7
1
13
null
null
2022-06-14
7
1
14
null
null
Fiddle

SQL creating overview for data that does not exist

I need a script that will give me a total sum of amounts per sort for the past 3 days and upcoming week.
I have made a script (link to dbfiddle.uk) that will sum the amount of the grouped date and sort types. However I am unclear on how to move forward at this point.
I want the past 3 days and upcoming 7 days of the today's date (dynamically), not just the dates found in my table.
I also want to show all the sort types.
So if there are no records for the date and sort type, show 0 as result.
plannings table
id date cell_id farmer_id
1 2020-04-21 1 1
2 2020-04-22 1 1
3 2020-04-24 1 1
4 2020-04-21 2 1
5 2020-04-22 2 1
6 2020-04-23 1 1
7 2020-04-25 1 1
8 2020-04-26 1 1
9 2020-04-22 4 1
10 2020-04-21 4 1
11 2020-04-23 4 1
planning_amounts table
id planning_id sort_type_id amount
2 1 1 43
3 1 3 34
4 2 1 54
5 3 1 45
6 4 1 90
7 5 3 45
8 5 1 99
9 6 1 66
10 7 1 999
11 8 3 90
12 9 1 23
13 10 1 43
14 11 1 55
sort_types table
id name description
1 Fijn Fijn
2 Middel Middel
3 Reuze Reuze
4 Industrie Industrie
The expected result would look like this. (this obviously for the past 3 + upcomming 7 days)
amount description date
176 Fijn 2020-04-21
34 Reuze 2020-04-21
0 Middel 2020-04-21
0 Industrie 2020-04-21
176 Fijn 2020-04-22
45 Reuze 2020-04-22
0 Middel 2020-04-22
0 Industrie 2020-04-22
121 Fijn 2020-04-23
0 Reuze 2020-04-23
0 Middel 2020-04-23
0 Industrie 2020-04-23
Query
SELECT SUM(amount) as amount, a.date, c.description
FROM planning_amounts b
join plannings a ON b.planning_id = a.id
join (SELECT * from sort_types) c ON b.sort_type_id = c.id
group by date, c.description
order by date
Hope I understood your question a bit better now:
WITH DesiredDates AS
(SELECT CAST(DATEADD(dd,-3,GETDATE()) as DATE) AS DesiredDate UNION ALL
SELECT CAST(DATEADD(dd,-2,GETDATE()) as DATE) UNION ALL
SELECT CAST(DATEADD(dd,-1,GETDATE()) as DATE) UNION ALL
SELECT CAST(GETDATE() as DATE) UNION ALL
SELECT CAST(DATEADD(dd,1,GETDATE()) as DATE) UNION ALL
SELECT CAST(DATEADD(dd,2,GETDATE()) as DATE) UNION ALL
SELECT CAST(DATEADD(dd,3,GETDATE()) as DATE) UNION ALL
SELECT CAST(DATEADD(dd,4,GETDATE()) as DATE) UNION ALL
SELECT CAST(DATEADD(dd,5,GETDATE()) as DATE) UNION ALL
SELECT CAST(DATEADD(dd,6,GETDATE()) as DATE) UNION ALL
SELECT CAST(DATEADD(dd,7,GETDATE()) as DATE)
), DesiredDatesAndSortTypes AS (
SELECT * FROM DesiredDates CROSS JOIN (select id from sort_types) t
)
SELECT SUM(ISNULL(Amount,0)) as Amount, DesiredDate, c.Description
FROM planning_amounts b
join plannings a ON b.planning_id = a.id
right join DesiredDatesAndSortTypes ddst ON CAST(a.date as DATE)=ddst.DesiredDate and b.sort_type_id=ddst.id
join sort_types c ON ddst.id = c.id
GROUP BY ddst.DesiredDate,c.Description
ORDER BY DesiredDate,Description
Here's the fiddle:
https://dbfiddle.uk/?rdbms=sqlserver_2019&fiddle=c508639ba4ec5bd49b49c9afe0692c9f
If I missunderstood yet again, please clarify where :)

Distinguish the first rows where a given column's value changes in a grouped result

I want to create a select query in SQL Server where I group the rows by a column (BaseId) and also order them by Status, RTime and Version. I want to add a column "isFirst" that has the value 1 if the BaseId value is the first in the group, and 0 if it's not.
My sample table:
Table name: Head
Id BaseId Name RTime Status Version
2 2 abc 04-12 12:34 1 1
3 3 xyz 04-12 13:10 9 1
4 2 abc 04-13 14:25 0 2
5 3 xyz 04-14 12:34 0 2
6 3 xyz 04-14 13:10 9 3
7 3 xyz 04-16 14:25 1 4
8 2 abc 04-16 17:40 1 3
9 9 sql 04-17 02:23 9 1
10 9 sql 04-17 07:31 0 2
Expected result:
isFirst Id BaseId Name RTime Status Version
1 10 9 sql 04-17 07:31 0 2
0 9 9 sql 04-17 02:23 9 1
1 5 3 xyz 04-14 12:34 0 2
0 7 3 xyz 04-16 14:25 1 4
0 6 3 xyz 04-14 13:10 9 3
0 3 3 xyz 04-12 13:10 9 1
1 4 2 abc 04-13 14:25 0 2
0 8 2 abc 04-16 17:40 1 3
0 2 2 abc 04-12 12:34 1 1
My query now looks like this:
SELECT *
FROM Head
ORDER BY BaseId desc, Status, RTime desc, Version desc
I think I should use CASE to create the isFirst column, but I've had no luck so far. Anyone could help me?
You can use row_number() and a case expression:
select
case when row_number() over(
partition by BaseId
order by Status, RTime desc, Version desc
) = 1
then 1
else 0
end isFirst,
h.*
from head h
order by BaseId desc, Status, RTime desc, Version desc

Restart Row Number Based on Date and Increments of N

I have a table with the following data that I generated with a date table
date day_num (DAY_NUM % 7)
2019-07-09 0 0
2019-07-10 1 1
2019-07-11 2 2
2019-07-12 3 3
2019-07-13 4 4
2019-07-14 5 5
2019-07-15 6 6
2019-07-16 7 0
I basically want to get a week number that restarts at 0 and I need help figuring out the last part
The final output would look like this
date day_num (DAY_NUM % 7) week num
2019-07-09 0 0 1
2019-07-10 1 1 1
2019-07-11 2 2 1
2019-07-12 3 3 1
2019-07-13 4 4 1
2019-07-14 5 5 1
2019-07-15 6 6 1
2019-07-16 7 0 2
This is the sql I have so far
select
SUB.*,
DAY_NUM%7
FROM(
SELECT
DISTINCT
id_date,
row_number() over(order by id_date) -1 as day_num
FROM schema.date_tbl
WHERE Id_date BETWEEN "2019-07-09" AND date_add("2019-07-09",146)
Building on your query:
select SUB.*, DAY_NUM%7,
DENSE_RANK() OVER (ORDER BY FLOOR(DAY_NUM / 7)) as weeknum
FROM (SELECT DISTINCT id_date,
row_number() over(order by id_date) -1 as day_num
FROM schema.date_tbl
WHERE Id_date BETWEEN "2019-07-09" AND date_add("2019-07-09", 146)
) x

SQL - Include a not exists line in a aggregation

I have a order table and I'm aggregation this table by this query:
SELECT dtdate, idsku, sum(vlorder) as vlorder, sum(qtditem) as qtditem
FROM order
GROUP BY dtdate, idsku
And I'm getting this results:
dtdate idsku vlorder qtditem
01/01/2016 1 4 8
02/01/2016 1 5 10
03/01/2016 1 3 6
04/01/2016 1 2 4
05/01/2016 1 3 6
06/01/2016 1 1 2
But, I don't have results for 07/01/2016 and idsku = 1 because doesn't exists on the database (sounds dummy). And I have to include this "empty" line 07/01/2016 1 0 0, like this:
dtdate idsku vlorder qtditem
01/01/2016 1 4 8
02/01/2016 1 5 10
03/01/2016 1 3 6
04/01/2016 1 2 4
05/01/2016 1 3 6
06/01/2016 1 1 2
07/01/2016 1 0 0
Is this possible?
In Postgres, you can use generate_series():
select g.mon, o.idsku,
sum(o.vlorder) as vlorder, sum(o.qtditem) as qtditem
from generate_series('2016-01-01'::timestamp, '2016-07-01'::timestamp,
interval '1' month) g(mon) left join
orders o
on o.dtdate = g.mon
group by g.mon, idsku;
If your dates are not on the first of the month, then you can use date_trunc('day', o.dtdate) = g.mon.