I have a table which has three columns shown in the picture.
Ord_dt - Date when the order was placed.
first_order- Date when the first order was placed.(Calculated based on last 52 weeks)
cnt_orders - Total Orders placed on Order Date.
ORD_DT first_order cnt_orders
6/19/2020 6/19/2020 2
6/22/2020 6/19/2020 1
10/8/2020 6/19/2020 2
11/20/2020 6/19/2020 1
12/1/2020 6/19/2020 1
2/4/2021 6/19/2020 1
2/12/2021 6/19/2020 1
3/7/2021 6/19/2020 1
3/30/2021 6/19/2020 1
4/7/2021 6/19/2020 1
4/30/2021 6/19/2020 1
5/11/2021 6/19/2020 1
5/31/2021 6/19/2020 2
7/28/2021 10/8/2020 2
The Final Output should be something like this based on First_order Date. Running_Sum column is a running sum of cnt_orders based on first_order.In the below example row 3 Ord_dt = first_order in row 14, so it should do a sum of all orders for row14 from row3 to row 14.
ORD_DT first_order cnt_orders Running_sum
6/19/2020 6/19/2020 2 2
6/22/2020 6/19/2020 1 3
10/8/2020 6/19/2020 2 5
11/20/2020 6/19/2020 1 6
12/1/2020 6/19/2020 1 7
2/4/2021 6/19/2020 1 8
2/12/2021 6/19/2020 1 9
3/7/2021 6/19/2020 1 10
3/30/2021 6/19/2020 1 11
4/7/2021 6/19/2020 1 12
4/30/2021 6/19/2020 1 13
5/11/2021 6/19/2020 1 14
5/31/2021 6/19/2020 2 16
7/28/2021 10/8/2020 2 15
I have tried with SUM and Partition but it doesn't give me the correct last row data since first_order has been changed. It should give me 15 instead of 18.
How can I achieve this in SQL Server?
Sample Table which required Running Sum
create table t
(ORD_DT date, first_order date, cnt_orders int);
go
insert into t values
('6/19/2020' , '6/19/2020' , 2),
('6/22/2020' , '6/19/2020' , 1),
('10/8/2020' , '6/19/2020' , 2),
('11/20/2020', '6/19/2020' , 1),
('12/1/2020' , '6/19/2020' , 1),
('2/4/2021' , '6/19/2020' , 1),
('2/12/2021' , '6/19/2020' , 1),
('3/7/2021' , '6/19/2020' , 1),
('3/30/2021' , '6/19/2020' , 1),
('4/7/2021' , '6/19/2020' , 1),
('4/30/2021' , '6/19/2020' , 1),
('5/11/2021' , '6/19/2020' , 1),
('5/31/2021' , '6/19/2020' , 2),
('7/28/2021' , '10/8/2020' , 2);
go
select * ,
(select sum(t1.cnt_orders)
from t t1
where t1.ord_dt >= t.first_order and
t1.ord_dt <= t.ORD_DT
) cumsum
from t
order by ord_dt;
ORD_DT first_order cnt_orders cumsum
---------- ----------- ----------- -----------
2020-06-19 2020-06-19 2 2
2020-06-22 2020-06-19 1 3
2020-10-08 2020-06-19 2 5
2020-11-20 2020-06-19 1 6
2020-12-01 2020-06-19 1 7
2021-02-04 2020-06-19 1 8
2021-02-12 2020-06-19 1 9
2021-03-07 2020-06-19 1 10
2021-03-30 2020-06-19 1 11
2021-04-07 2020-06-19 1 12
2021-04-30 2020-06-19 1 13
2021-05-11 2020-06-19 1 14
2021-05-31 2020-06-19 2 16
2021-07-28 2020-10-08 2 15
(14 row(s) affected)
Related
I am trying to create a dates table in SQL based on a set of inputs, but I haven't been able to figure it out.
I am receiving in SQL inputs as below:
This table:
Date
Value
2022-01-01
5
2022-07-12
10
2022-11-15
3
A Start Date = 2022-01-01
A stop Date = 2022-12-01
I need to get a table as below starting from Start Date until Stop Date, assiging each correspondent number based on the initial table to each date in that period:
Date
Value
2022-01-01
5
2022-01-02
5
2022-01-03
5
2022-01-04
5
.
5
.
5
.
5
2022-07-09
5
2022-07-10
5
2022-07-11
5
2022-07-12
10
2022-07-13
10
2022-07-14
10
.
10
.
10
2022-11-13
10
2022-11-14
10
2022-11-15
3
2022-11-16
3
2022-11-17
3
2022-11-18
3
How can I do that?
Thanks.
Using the window function lead() over() in concert with an ad-hoc tally table
Example
Select Date = dateadd(DAY,N,A.Date)
,A.Value
From (
Select *
,nDays = datediff(DAY,Date,lead(Date,1,dateadd(day,1,'2022-12-01')) over (order by date))
From YourTable
) A
Join ( Select Top 1000 N=-1+Row_Number() Over (Order By (Select NULL)) From master..spt_values n1, master..spt_values n2 ) B
on N<NDays
Order by Date
Results
Date Value
2022-01-01 5
2022-01-02 5
2022-01-03 5
2022-01-04 5
2022-01-05 5
...
2022-07-10 5
2022-07-11 5
2022-07-12 10
2022-07-13 10
2022-07-14 10
...
2022-11-12 10
2022-11-13 10
2022-11-14 10
2022-11-15 3
2022-11-16 3
2022-11-17 3
...
2022-11-30 3
2022-12-01 3
I need to count the number of campaigns per day based on the start and end dates of the campaigns
Input Table:
Campaign name
Start date
End date
Campaign A
2022-07-10
2022-09-25
Campaign B
2022-08-06
2022-10-07
Campaign C
2022-07-30
2022-09-10
Campaign D
2022-08-26
2022-10-24
Campaign E
2022-07-17
2022-09-29
Campaign F
2022-08-24
2022-09-12
Campaign G
2022-08-11
2022-10-24
Campaign H
2022-08-26
2022-11-22
Campaign I
2022-08-29
2022-09-25
Campaign J
2022-08-21
2022-11-15
Campaign K
2022-07-20
2022-09-18
Campaign L
2022-07-31
2022-11-20
Campaign M
2022-08-17
2022-10-10
Campaign N
2022-07-27
2022-09-07
Campaign O
2022-07-29
2022-09-26
Campaign P
2022-07-06
2022-09-15
Campaign Q
2022-07-16
2022-09-22
Out needed (result):
Date
Count unique campaigns
2022-07-02
17
2022-07-03
47
2022-07-04
5
2022-07-05
5
2022-07-06
25
2022-07-07
27
2022-07-08
17
2022-07-09
58
2022-07-10
23
2022-07-11
53
2022-07-12
18
2022-07-13
29
2022-07-14
52
2022-07-15
7
2022-07-16
17
2022-07-17
37
2022-07-18
33
How do I need to write the SQL command to get the above result? thanks all
In the following solutions we leverage string_split with combination with replicate to generate new records.
select dt as date
,count(*) as Count_unique_campaigns
from
(
select *
,dateadd(day, row_number() over(partition by Campaign_name order by (select null))-1, Start_date) as dt
from (
select *
from t
outer apply string_split(replicate(',',datediff(day, Start_date, End_date)),',')
) t
) t
group by dt
order by dt
date
Count_unique_campaigns
2022-07-06
1
2022-07-07
1
2022-07-08
1
2022-07-09
1
2022-07-10
2
2022-07-11
2
2022-07-12
2
2022-07-13
2
2022-07-14
2
2022-07-15
2
2022-07-16
3
2022-07-17
4
2022-07-18
4
2022-07-19
4
2022-07-20
5
2022-07-21
5
2022-07-22
5
2022-07-23
5
2022-07-24
5
2022-07-25
5
2022-07-26
5
2022-07-27
6
2022-07-28
6
2022-07-29
7
2022-07-30
8
2022-07-31
9
2022-08-01
9
2022-08-02
9
2022-08-03
9
2022-08-04
9
2022-08-05
9
2022-08-06
10
2022-08-07
10
2022-08-08
10
2022-08-09
10
2022-08-10
10
2022-08-11
11
2022-08-12
11
2022-08-13
11
2022-08-14
11
2022-08-15
11
2022-08-16
11
2022-08-17
12
2022-08-18
12
2022-08-19
12
2022-08-20
12
2022-08-21
13
2022-08-22
13
2022-08-23
13
2022-08-24
14
2022-08-25
14
2022-08-26
16
2022-08-27
16
2022-08-28
16
2022-08-29
17
2022-08-30
17
2022-08-31
17
2022-09-01
17
2022-09-02
17
2022-09-03
17
2022-09-04
17
2022-09-05
17
2022-09-06
17
2022-09-07
17
2022-09-08
16
2022-09-09
16
2022-09-10
16
2022-09-11
15
2022-09-12
15
2022-09-13
14
2022-09-14
14
2022-09-15
14
2022-09-16
13
2022-09-17
13
2022-09-18
13
2022-09-19
12
2022-09-20
12
2022-09-21
12
2022-09-22
12
2022-09-23
11
2022-09-24
11
2022-09-25
11
2022-09-26
9
2022-09-27
8
2022-09-28
8
2022-09-29
8
2022-09-30
7
2022-10-01
7
2022-10-02
7
2022-10-03
7
2022-10-04
7
2022-10-05
7
2022-10-06
7
2022-10-07
7
2022-10-08
6
2022-10-09
6
2022-10-10
6
2022-10-11
5
2022-10-12
5
2022-10-13
5
2022-10-14
5
2022-10-15
5
2022-10-16
5
2022-10-17
5
2022-10-18
5
2022-10-19
5
2022-10-20
5
2022-10-21
5
2022-10-22
5
2022-10-23
5
2022-10-24
5
2022-10-25
3
2022-10-26
3
2022-10-27
3
2022-10-28
3
2022-10-29
3
2022-10-30
3
2022-10-31
3
2022-11-01
3
2022-11-02
3
2022-11-03
3
2022-11-04
3
2022-11-05
3
2022-11-06
3
2022-11-07
3
2022-11-08
3
2022-11-09
3
2022-11-10
3
2022-11-11
3
2022-11-12
3
2022-11-13
3
2022-11-14
3
2022-11-15
3
2022-11-16
2
2022-11-17
2
2022-11-18
2
2022-11-19
2
2022-11-20
2
2022-11-21
1
2022-11-22
1
For SQL in Azure and SQL Server 2022 we have a cleaner solution based on [ordinal][4].
"The enable_ordinal argument and ordinal output column are currently
supported in Azure SQL Database, Azure SQL Managed Instance, and Azure
Synapse Analytics (serverless SQL pool only). Beginning with SQL
Server 2022 (16.x) Preview, the argument and output column are
available in SQL Server."
select dt as date
,count(*) as Count_unique_campaigns
from
(
select *
,dateadd(day, ordinal-1, Start_date) as dt
from (
select *
from t
outer apply string_split(replicate(',',datediff(day, Start_date, End_date)),',', 1)
) t
) t
group by dt
order by dt
Fiddle
Your sample data doesn't seem to match your desired results, but I think what you're after is this:
DECLARE #Start date, #End date;
-- first, find the earliest and last date:
SELECT #Start = MIN([Start date]), #End = MAX([End date])
FROM dbo.Campaigns;
-- now use a recursive CTE to build a date range,
-- and count the number of campaigns that have a row
-- where the campaign was active on that date:
WITH d(d) AS
(
SELECT #Start
UNION ALL
SELECT DATEADD(DAY, 1, d) FROM d WHERE d < #End
)
SELECT
[Date] = d,
[Count unique campaigns] = COUNT(*)
FROM d
INNER JOIN dbo.Campaigns AS c
ON d.d >= c.[Start date] AND d.d <= c.[End date]
GROUP BY d.d OPTION (MAXRECURSION 32767);
Working example in this fiddle.
Using SQL Server Management Studio v17.9.1
I'm trying to rank / order / group some data by Site and Area by Date, but I'm struggling to get my head around not ranking the area alphabetically and ranking it by the earliest date it appears.
Here's the data I have:
Site | Area | Space | Date
DCG X 7 02/02/2020 12:13
DCG X 5 04/02/2020 11:47
DCG X 12 10/02/2020 15:14
GNL U 0 03/03/2020 18:35
GNL A 4 04/03/2020 08:28
GNL C 4 06/03/2020 09:07
GNL B 1 16/03/2020 07:10
DPL U 0 18/03/2020 09:28
DPL A 1 18/03/2020 09:36
DPL A 1 20/03/2020 20:04
SGR F 2 21/03/2020 19:42
SGR B 2 22/03/2020 10:30
SGR C 3 24/03/2020 08:17
SGR F 1 01/04/2020 09:00
SGR E 1 02/02/2020 10:57
SGR F 1 02/02/2020 15:50
I want to add 2 columns that rank / group the site and the area in ascending order of date, like so:
Site | Area | Space | Date | Site Order | Area Order |
DCG X 7 02/02/2020 12:13 1 1
DCG X 5 04/02/2020 11:47 1 1
DCG X 12 10/02/2020 15:14 1 1
GNL U 0 03/03/2020 18:35 2 1
GNL A 4 04/03/2020 08:28 2 2
GNL C 4 06/03/2020 09:07 2 3
GNL B 1 16/03/2020 07:10 2 4
DPL U 0 18/03/2020 09:28 3 1
DPL A 1 18/03/2020 09:36 3 2
DPL A 1 20/03/2020 20:04 3 2
SGR F 2 21/03/2020 19:42 4 1
SGR B 2 22/03/2020 10:30 4 2
SGR C 3 24/03/2020 08:17 4 3
SGR F 1 01/04/2020 09:00 4 1
SGR E 1 02/02/2020 10:57 4 4
SGR F 1 02/02/2020 15:50 4 1
Apologies if I've not made it clear
You can use min() as a window function to get the minimum date for each site and site/area combo. Then use dense_rank():
select t.*,
dense_rank() over (order by min_site_date, site) as site_seqnum,
dense_rank() over (partition by site order by min_site_date) as area_seqnum
from (select t.*,
min(date) over (partition by site) as min_site_date,
min(date) over (partition by site, area) as min_site_area_date
from t
) t
You can use window function :
select t.*,
dense_rank() over (order by site, site_date) as site_sequence,
dense_rank() over (partition by site order by area, site_area_date) as area_sequence
from (select t.*,
min([date]) over (partition by [site]) as site_date,
min([date]) over (partition by [site], area) as site_area_date
from table t
) t;
I have to do in sql oracle a time scheduling query. For each employee, I have to show which day he is working or not. Sometimes the person can have a work schedule on two weeks. For example in week 1 he doesn't work Monday, Tuesday and Friday and the week 2 he doesn't work at Monday and Friday.
I already get the query but sometimes the result doesn't start at Monday.
I want to add a weeknumber so I am able to order. Example:
empl date day_nbr Working
1 01/11/2017 3 FULLDAY
1 02/11/2017 4 FULLDAY
1 03/11/2017 5 OFF
1 04/11/2017 6 OFF
1 05/11/2017 7 OFF
1 06/11/2017 1 OFF
1 07/11/2017 2 FULLDAY
1 08/11/2017 3 FULLDAY
1 09/11/2017 4 FULLDAY
1 10/11/2017 5 OFF
1 11/11/2017 6 OFF
1 12/11/2017 7 OFF
1 13/11/2017 1 OFF
1 14/11/2017 2 OFF
2 01/09/2018 6 OFF
2 02/09/2018 7 OFF
2 03/09/2018 1 FULLDAY
2 04/09/2018 2 FULLDAY
2 05/09/2018 3 OFF
2 06/09/2018 4 FULLDAY
2 07/09/2018 5 FULLDAY
For empl=1 you see the week begins at day_nbr 3. I want to add a column weeknumber so I have following result:
empl date day_nbr Working weeknumber
1 01/11/2017 3 FULLDAY 1
1 02/11/2017 4 FULLDAY 1
1 03/11/2017 5 OFF 1
1 04/11/2017 6 OFF 1
1 05/11/2017 7 OFF 1
1 06/11/2017 1 OFF 2
1 07/11/2017 2 FULLDAY 2
1 08/11/2017 3 FULLDAY 2
1 09/11/2017 4 FULLDAY 2
1 10/11/2017 5 OFF 2
1 11/11/2017 6 OFF 2
1 12/11/2017 7 OFF 1
1 13/11/2017 1 OFF 1
1 14/11/2017 2 OFF 1
2 01/09/2018 6 OFF 1
2 02/09/2018 7 OFF 1
2 03/09/2018 1 FULLDAY 1
2 04/09/2018 2 FULLDAY 1
2 05/09/2018 3 OFF 1
2 06/09/2018 4 FULLDAY 1
2 07/09/2018 5 FULLDAY 1
In that way I can order on weeknumber, day_nbr and I will always start at day_nbr 1.
The reason I want to do that is to be able to make a pivot table like this for empl=1
But the problem I get this:
I need to create a new grouping every time I have a period of more than 60 days since my previous record.
Basically, I need too take the data I have here:
RowNo StartDate StopDate DaysBetween
1 3/21/2017 3/21/2017 14
2 4/4/2017 4/4/2017 14
3 4/18/2017 4/18/2017 14
4 6/23/2017 6/23/2017 66
5 7/5/2017 7/5/2017 12
6 7/19/2017 7/19/2017 14
7 9/27/2017 9/27/2017 70
8 10/24/2017 10/24/2017 27
9 10/31/2017 10/31/2017 7
10 11/14/2017 11/14/2017 14
And turn it into this:
RowNo StartDate StopDate DaysBetween Series
1 3/21/2017 3/21/2017 14 1
2 4/4/2017 4/4/2017 14 1
3 4/18/2017 4/18/2017 14 1
4 6/23/2017 6/23/2017 66 2
5 7/5/2017 7/5/2017 12 2
6 7/19/2017 7/19/2017 14 2
7 9/27/2017 9/27/2017 70 3
8 10/24/2017 10/24/2017 27 3
9 10/31/2017 10/31/2017 7 3
10 11/14/2017 11/14/2017 14 3
Once I have that I'll group by Series and get the min(StartDate) and max(StopDate) for individual durations.
I could do this using a cursor but I'm sure someone much smarter than me has figured out a more elegant solution. Thanks in advance!
You can use the window function sum() over with a conditional FLAG
Example
Select *
,Series= 1+sum(case when [DaysBetween]>60 then 1 else 0 end) over (Order by RowNo)
From YourTable
Returns
RowNo StartDate StopDate DaysBetween Series
1 2017-03-21 2017-03-21 14 1
2 2017-04-04 2017-04-04 14 1
3 2017-04-18 2017-04-18 14 1
4 2017-06-23 2017-06-23 66 2
5 2017-07-05 2017-07-05 12 2
6 2017-07-19 2017-07-19 14 2
7 2017-09-27 2017-09-27 70 3
8 2017-10-24 2017-10-24 27 3
9 2017-10-31 2017-10-31 7 3
10 2017-11-14 2017-11-14 14 3
EDIT - 2008 Version
Select A.*
,B.*
From YourTable A
Cross Apply (
Select Series=1+sum( case when [DaysBetween]>60 then 1 else 0 end)
From YourTable
Where RowNo <= A.RowNo
) B