How can I dynamically create dates between a specific timespan and weeks? - sql

I have the following customer table:
ID | StartDate | WeekCount
1 | 01.12.2015 | 2
2 | 03.12.2015 | 4
3 | 06.06.2014 | 8
The Startdate represents the date the customer gets the first visit, WeekCount is for the next visit (every X Weeks)
I want to query the next visit dates for a timespawn.
Lets say the first visit is 03.12.2015 then I query for March 2016 so the expected date should be 03.03.2015.
So basically StartDate+WeekCount and then the Date between filter.

I think recursive CTE will help you to solve your problem.
DECLARE #to_date DATETIME
SET #to_date = N'2016.03.01'
;WITH test_data AS(
SELECT 1 AS id, CAST(N'2015.12.01' AS DATETIME) AS startDate, 2 AS weekCount
UNION ALL
SELECT 2 AS id, CAST(N'2015.12.03' AS DATETIME) AS startDate, 4 AS weekCount
UNION ALL
SELECT 3 AS id, CAST(N'2014.06.06' AS DATETIME) AS startDate, 8 AS weekCount
),
result_tbl AS(
SELECT id, startDate, weekCount FROM test_data
UNION ALL
SELECT id, DATEADD(ww, R.weekCount, R.startDate), weekCount FROM result_tbl AS R
WHERE R.startDate < #to_date
)
SELECT * FROM result_tbl
ORDER BY id

Provided the datatype is date/datetime
Select columns from your_table
where StartDate>='20160301' and StartDate<'20160401'

Related

Split dates into quarters based on start and end date

I want to split quarters based on a given start and end date.
I have the following table:
table1
ID
start_date
end_date
No. of Quarters
1
01-01-2017
01-01-2018
4
2
01-04-2017
01-10-2018
7
So the result table should be have dates split based on number of quarters and end date.
The result table should look like:
table2
ID
Quarterly Start Date
1
01-01-2017
1
01-04-2017
1
01-07-2017
1
01-10-2017
2
01-04-2017
2
01-07-2017
2
01-10-2017
2
01-01-2018
2
01-04-2018
2
01-07-2018
2
01-10-2018
I found a solution on stackoverflow which states
declare #startDate datetime
declare #endDate datetime
select
#startDate= ET.start_date,
#endDate= ET.end_date
from
table1
;With cte
As
( Select #startDate date1
Union All
Select DateAdd(Month,3,date1) From cte where date1 < #endDate
) select cast(cast( Year(date1)*10000 + MONTH(date1)*100 + 1 as
varchar(255)) as date) quarterlyDates From cte
Since I am new to sql, I am having troubles customizing it to my problem.
Could anyone please recommend a way? Thanks!
If I understand correctly, the recursive CTE would look like:
with cte as (
select id, start_date, num_quarters
from t
union all
select id, dateadd(month, 3, start_date), num_quarters - 1
from cte
where num_quarters > 1
)
select *
from cte;
Here is a db<>fiddle.

How to duplicate data in sql with conditions

I havea table as table_A . table_A includes these columns
-CountryName
-Min_Date
-Max_Date
-Number
I want to duplicate data with seperating by months. For example
Argentina | 2015-01-04 | 2015-04-07 | 100
England | 2015-02-08 | 2015-03-11 | 90
I want to see a table as this (Monthly seperated)
Argentina | 01-2015 | 27 //(days to end of the min_date's month)
Argentina | 02-2015 | 29 //(days full month)
Argentina | 03-2015 | 31 //(days full month)
Argentina | 04-2015 | 7 //(days from start of the max_date's month)
England | 02-2015 | 21 //(days)
England | 03-2015 | 11 //(days)
I tried too much thing to made this for each records. But now my brain is so confusing and my project is delaying.
Does anybody know how can i solve this. I tried to duplicate each rows with datediff count but it is not working
WITH cte AS (
SELECT CountryName, ISNULL(DATEDIFF(M,Min_Date ,Max_Date )+1,1) as count FROM table_A
UNION ALL
SELECT CountryName, count-1 FROM cte WHERE count>1
)
SELECT CountryName,count FROM cte
-Generate all the dates between min and max dates for each country.
-Then get the month start and month end dates for each country,year,month.
-Finally get the date differences of the month start and month end.
WITH cte AS (
SELECT Country, min_date dt,min_date,max_date FROM t
UNION ALL
SELECT Country, dateadd(dd,1,dt),min_date,max_date FROM cte WHERE dt < max_date
)
,monthends as (
SELECT country,year(dt) yr,month(dt) mth,max(dt) monthend,min(dt) monthstart
FROM cte
GROUP BY country,year(dt),month(dt))
select country
,cast(mth as varchar(2))+'-'+cast(yr as varchar(4)) yr_month
,datediff(dd,monthstart,monthend)+1 days_diff
from monthends
Sample Demo
EDIT: Another option would be to generate all the dates once (the example shown here generates 51 years of dates from 2000 to 2050) and then joining it to the table to get the days by month.
WITH cte AS (
SELECT cast('2000-01-01' as date) dt,cast('2050-12-31' as date) maxdt
UNION ALL
SELECT dateadd(dd,1,dt),maxdt FROM cte WHERE dt < maxdt
)
SELECT country,year(dt) yr,month(dt) mth, datediff(dd,min(dt),max(dt))+1 days_diff
FROM cte c
JOIN t on c.dt BETWEEN t.min_date and t.max_date
GROUP BY country,year(dt),month(dt)
OPTION (MAXRECURSION 0)
I think you have the right idea. But you need to construct the months:
WITH cte AS (
SELECT CountryName, Min_Date as dte, Min_Date, Max_Date
FROM table_A
UNION ALL
SELECT CountryName, DATEADD(month, 1, dte), Min_Date, Max_Date
FROM cte
WHERE dte < Max_date
)
SELECT CountryName, dte
FROM cte;
Getting the number of days in the month is a bit more complicated. That requires some thought.
Oh, I forgot about EOMONTH():
select countryName, dte,
(case when dte = min_date
then datediff(day, min_date, eomonth(dte)) + 1
when dte = max_date
then day(dte)
else day(eomonth(dte))
end) as days
from cte;
Using a Calendar Table makes this stuff pretty easy. RexTester: http://rextester.com/EBTIMG23993
begin
create table #enderaric (
CountryName varchar(16)
, Min_Date date
, Max_Date date
, Number int
)
insert into #enderaric values
('Argentina' ,'2015-01-04' ,'2015-04-07' ,'100')
, ('England' ,'2015-02-08' ,'2015-03-11' ,'90')
end;
-- select * from #enderaric
--*/"
declare #FromDate date;
declare #ThruDate date;
set #FromDate = '2015-01-01';
set #ThruDate = '2015-12-31';
with x as (
select top (cast(sqrt(datediff(day, #FromDate, #ThruDate)) as int) + 1)
[number]
from [master]..spt_values v
)
/* Date Range CTE */
,cal as (
select top (1+datediff(day, #FromDate, #ThruDate))
DateValue = convert(date,dateadd(day,
row_number() over (order by x.number)-1,#FromDate)
)
from x cross join x as y
order by DateValue
)
select
e.CountryName
, YearMonth = convert(char(7),left(convert(varchar(10),DateValue),7))
, [Days]=count(c.DateValue)
from #enderaric as e
inner join cal c on c.DateValue >= e.min_date
and c.DateValue <= e.max_date
group by
e.CountryName
, e.Min_Date
, e.Max_Date
, e.Number
, convert(char(7),left(convert(varchar(10),DateValue),7))
results in:
CountryName YearMonth Days
---------------- --------- -----------
Argentina 2015-01 28
Argentina 2015-02 28
Argentina 2015-03 31
Argentina 2015-04 7
England 2015-02 21
England 2015-03 11
More about calendar tables:
Aaron Bertrand - Generate a set or sequence without loops
generate-a-set-1
generate-a-set-2
generate-a-set-3
David Stein - Creating a Date Table/Dimension on SQL 2008
Michael Valentine Jones - F_TABLE_DATE

Query to return all the days of a month

This problem is related to this, which has no solution in sight: here
I have a table that shows me all sessions of an area.
This session has a start date.
I need to get all the days of month of the start date of the session by specific area (in this case)
I have this query:
SELECT idArea, idSession, startDate FROM SessionsPerArea WHERE idArea = 1
idArea | idSession | startDate |
1 | 1 | 01-01-2013 |
1 | 2 | 04-01-2013 |
1 | 3 | 07-02-2013 |
And i want something like this:
date | Session |
01-01-2013 | 1 |
02-01-2013 | NULL |
03-01-2013 | NULL |
04-01-2013 | 1 |
........ | |
29-01-2013 | NULL |
30-01-2013 | NULL |
In this case, the table returns me all the days of January.
The second column is the number of sessions that occur on that day, because there may be several sessions on the same day.
Anyone can help me?
Please try:
DECLARE #SessionsPerArea TABLE (idArea INT, idSession INT, startDate DATEtime)
INSERT #SessionsPerArea VALUES (1,1,'2013-01-01')
INSERT #SessionsPerArea VALUES (1,2,'2013-01-04')
INSERT #SessionsPerArea VALUES (1,3,'2013-07-02')
DECLARE #RepMonth as datetime
SET #RepMonth = '01/01/2013';
WITH DayList (DayDate) AS
(
SELECT #RepMonth
UNION ALL
SELECT DATEADD(d, 1, DayDate)
FROM DayList
WHERE (DayDate < DATEADD(d, -1, DATEADD(m, 1, #RepMonth)))
)
SELECT *
FROM DayList t1 left join #SessionsPerArea t2 on t1.DayDate=startDate and t2.idArea = 1
This will work:
DECLARE #SessionsPerArea TABLE (idArea INT, idSession INT, startDate DATE)
INSERT #SessionsPerArea VALUES
(1,1,'2013-01-01'),
(1,2,'2013-01-04'),
(1,3,'2013-07-02')
;WITH t1 AS
(
SELECT startDate
, DATEADD(MONTH, DATEDIFF(MONTH, '1900-01-01', startDate), '1900-01-01') firstInMonth
, DATEADD(DAY, -1, DATEADD(MONTH, DATEDIFF(MONTH, '1900-01-01', startDate) + 1, '1900-01-01')) lastInMonth
, COUNT(*) cnt
FROM #SessionsPerArea
WHERE idArea = 1
GROUP BY
startDate
)
, calendar AS
(
SELECT DISTINCT DATEADD(DAY, c.number, t1.firstInMonth) d
FROM t1
JOIN master..spt_values c ON
type = 'P'
AND DATEADD(DAY, c.number, t1.firstInMonth) BETWEEN t1.firstInMonth AND t1.lastInMonth
)
SELECT d date
, cnt Session
FROM calendar c
LEFT JOIN t1 ON t1.startDate = c.d
It uses simple join on master..spt_values table to generate rows.
Just an example of calendar table. To return data for a month adjust the number of days between < 32, for a year to 365+1. You can calculate the number of days in a month or between start/end dates with query. I'm not sure how to do this in SQL Server. I'm using hardcoded values to display all dates in Jan-2013. You can adjust start and end dates for diff. month or to get start/end dates with queries...:
WITH data(r, start_date) AS
(
SELECT 1 r, date '2012-12-31' start_date FROM any_table --dual in Oracle
UNION ALL
SELECT r+1, date '2013-01-01'+r-1 FROM data WHERE r < 32 -- number of days between start and end date+1
)
SELECT start_date FROM data WHERE r > 1
/
START_DATE
----------
1/1/2013
1/2/2013
1/3/2013
...
...
1/31/2013

How to group by Date Range starting from initial date

I have the following table structure
Key int
MemberID int
VisitDate DateTime
How can group all the dates falling with a given date range say 15 days..The first visit for the sameMember should be considered as the starting date.
eg
Key ID VisitDate(MM/dd/YY)
1 1 02/01/11
2 1 02/09/11
3 1 02/12/11
4 1 02/17/11
5 2 02/03/11
6 2 02/19/11
In this case the result should be
ID StartDate EndDate
1 02/01/11 02/12/11
1 02/17/11 02/17/11
2 02/03/11 02/03/11
2 02/19/11 02/19/11
One way to do this would be to use window aggregating. Here's how:
Setup:
DECLARE #data TABLE (
[Key] int, ID int, VisitDate date
);
INSERT INTO #data ([Key], ID, VisitDate)
SELECT 1, 1, '02/01/2011' UNION ALL
SELECT 2, 1, '02/09/2011' UNION ALL
SELECT 3, 1, '02/12/2011' UNION ALL
SELECT 4, 1, '02/17/2011' UNION ALL
SELECT 5, 2, '02/03/2011' UNION ALL
SELECT 6, 2, '02/19/2011';
Query:
WITH marked AS (
SELECT
*,
Grp = DATEDIFF(DAY, MIN(VisitDate) OVER (PARTITION BY ID), VisitDate) / 15
FROM #data
)
SELECT
ID,
StartDate = MIN(VisitDate),
EndDate = MAX(VisitDate)
FROM marked
GROUP BY ID, Grp
ORDER BY ID, StartDate
Output:
ID StartDate EndDate
----------- ---------- ----------
1 2011-02-01 2011-02-12
1 2011-02-17 2011-02-17
2 2011-02-03 2011-02-03
2 2011-02-19 2011-02-19
Basically, for each row, the query is calculating the difference of days between VisitDate and the first VisitDate for the same ID and divides it by 15. The result is then used as a grouping criterion. Note that SQL Server uses integer division when both operands of the / operator are integers.

Select rows where price didn't change

Suppose you have a table like (am using SQL Server 2008, no audit log - table is HUGE):
SecID | Date | Price
1 1/1/11 10
1 1/2/11 10
1 1/3/11 5
1 1/4/11 10
1 1/5/11 10
Suppose this table is HUGE (millions of rows for different secIDs and Date) - I would like to return the records when the price changed (looking for something better than using a cursor and iterating):
Am trying to figure out how to get:
SecID | StartDate | EndDate | Price
1 1/1/11 1/2/11 10
1 1/3/11 1/3/11 5
1 1/4/11 1/5/11 10
i.e. another way to look at it is that I am looking for a range of dates where the price has stayed the same.
This is an "islands" problem.
declare #Yourtable table
(SecID int, Date Date, Price int)
INSERT INTO #Yourtable
SELECT 1,GETDATE()-5,10 union all
SELECT 1,GETDATE()-4,10 union all
SELECT 1,GETDATE()-3,5 union all
SELECT 1,GETDATE()-2,10 union all
SELECT 1,GETDATE()-1, 10
;WITH cte AS
(
SELECT SecID,Date,Price,
ROW_NUMBER() OVER (PARTITION BY SecID ORDER BY Date) -
ROW_NUMBER() OVER (PARTITION BY Price, SecID ORDER BY Date) AS Grp
FROM #Yourtable
)
SELECT SecID,Price, MIN(Date) StartDate, MAX(Date) EndDate
FROM cte
GROUP BY SecID, Grp, Price
ORDER BY SecID, MIN(Date)
If the value does not change, the std deviation will be zero
select secId
from ...
group by secId
having count(*) = 1
OR stdev(price) = 0
I think this should work
SELECT SecID, Min(Date) AS StartDate, Max(Date) AS EndDate, Price FROM BigTable GROUP BY SecID, EndDate Having Min(Date) != MAx(Date) And Date != NULL