SQL - result fom weekday(time) and from week(without-Time) - sql

I'm trying to solve the problem. I need to get results from a working week (working hours are specified, for example, from 7:00 am to 8:00 pm), and I need to include weekends results (without specified time). But I dont know how to solve problem with weekday and include to result
select
c.PatientId
,g.PatientId
,c.StartDate
,g.TransferOrdinalNumber
from ClinicalEvent C
join PatientIncomming G on C.PatientId=G.PatientId
where
(DATEPART(dw, StartDate) in (1,2,3,4,5))
and (
(CAST(StartDate as Time) between '20:00' and '23:59')
or
(CAST(StartDate as Time) between '00:01' and '07:00')
)
and StartDate between '2020.02.01' and '2020.2.28'
and EventTypeId = '5365f53c-583b-4b53-bf50-2ec7c002e53c'
and g.TransferOrdinalNumber=1
group by C.PatientId, G.PatientId, c.StartDate, g.TransferOrdinalNumber
order by C.PatientId, G.PatientId

You can use UNION ALL statement can be helpful in the very basic method.
select
c.PatientId
,g.PatientId
,c.StartDate
,g.TransferOrdinalNumber
from ClinicalEvent C
join PatientIncomming G on C.PatientId=G.PatientId
where
(DATEPART(dw, StartDate) in (1,2,3,4,5))
and (
(CAST(StartDate as Time) between '20:00' and '23:59')
or
(CAST(StartDate as Time) between '00:01' and '07:00')
)
and StartDate between '2020.02.01' and '2020.2.28'
and EventTypeId = '5365f53c-583b-4b53-bf50-2ec7c002e53c'
and g.TransferOrdinalNumber=1
group by C.PatientId, G.PatientId, c.StartDate, g.TransferOrdinalNumber
UNION ALL
select
c.PatientId
,g.PatientId
,c.StartDate
,g.TransferOrdinalNumber
from ClinicalEvent C
join PatientIncomming G on C.PatientId=G.PatientId
where
(DATEPART(dw, StartDate) in (6,7))
--and (
--(CAST(StartDate as Time) between '20:00' and '23:59')
--or
--(CAST(StartDate as Time) between '00:01' and '07:00')
--)
and StartDate between '2020.02.01' and '2020.2.28'
and EventTypeId = '5365f53c-583b-4b53-bf50-2ec7c002e53c'
and g.TransferOrdinalNumber=1
group by C.PatientId, G.PatientId, c.StartDate, g.TransferOrdinalNumber
order by C.PatientId, G.PatientId

Related

Efficient sql subquery on same table based off datetime value

Below I have a simple query to get all the movie ratings for today joining an "event" table and "movie" table.
Select e.*, m.moviename
From Event e, movie m
Where e.eventdate >= DATEADD(day, -1, GETDATE())
and e.moviekey = m.moviekey
order by e.Ratings desc;
Question
In the example above, how would you retrieve the ratings from 1 week ago, and 1 month ago. So the query would return 2 extra columns RatingOneMonthAgo, RatingsOneWeekAgo,etc.
I've looked into subqueries and it's not clicking any help would be appreciated.
Thanks
You could use CTEs to pull this information in (similar to using subqueries).
The following query assumes that you having ratings for every day, and no duplicates (multiple ratings for the same movie on the same day):
WITH cteOneWeekAgo
AS
(
SELECT
moviekey
, Ratings
FROM Event
WHERE CAST(eventdate AS date) = DATEADD(WEEK, -1, CAST(GETDATE() AS date))
)
,
cteOneMonthAgo
AS
(
SELECT
moviekey
, Ratings
FROM Event
WHERE CAST(eventdate AS date) = DATEADD(MONTH, -1, CAST(GETDATE() AS date))
)
SELECT
e.*
, m.moviename
, w.Ratings Ratings_OneWeekAgo
, mth.Ratings Ratings_OneMonthAgo
FROM
Event e
JOIN movie m ON e.moviekey = m.moviekey
LEFT JOIN cteOneWeekAgo w ON e.moviekey = w.moviekey
LEFT JOIN cteOneMonthAgo mth ON e.moviekey = mth.moviekey
WHERE e.eventdate >= DATEADD(DAY, -1, GETDATE())
ORDER BY e.Ratings DESC
I also wrote a more complex query, which will pull in the most recent ratings for the movie before the date you're looking for if ratings for that date don't exist.
WITH cteOneWeekAgo
AS
(
SELECT
moviekey
, Ratings
, eventdate
FROM
(
SELECT
moviekey
, Ratings
, eventdate
, ROW_NUMBER() OVER (PARTITION BY moviekey ORDER BY eventdate DESC) R
FROM Event
WHERE CAST(eventdate AS date) <= DATEADD(WEEK, -1, CAST(GETDATE() AS date))
) Q
WHERE R = 1
)
,
cteOneMonthAgo
AS
(
SELECT
moviekey
, Ratings
, eventdate
FROM
(
SELECT
moviekey
, Ratings
, eventdate
, ROW_NUMBER() OVER (PARTITION BY moviekey ORDER BY eventdate DESC) R
FROM Event
WHERE CAST(eventdate AS date) <= DATEADD(MONTH, -1, CAST(GETDATE() AS date))
) Q
WHERE R = 1
)
SELECT
e.*
, m.moviename
, w.eventdate Ratings_OneWeekAgo_MostRecentDate
, w.Ratings Ratings_OneWeekAgo
, mth.eventdate Ratings_OneMonthAgo_MostRecentDate
, mth.Ratings Ratings_OneMonthAgo
FROM
Event e
JOIN movie m ON e.moviekey = m.moviekey
LEFT JOIN cteOneWeekAgo w ON e.moviekey = w.moviekey
LEFT JOIN cteOneMonthAgo mth ON e.moviekey = mth.moviekey
WHERE e.eventdate >= DATEADD(DAY, -1, GETDATE())
ORDER BY e.Ratings DESC

Aggregate function at Subquery And Performance Issue

I have query which i should select records from 1 table to insert another table once everyday(Select Into). It's big and enough complicated query for me because of i want to do it it 1 query. Here is piece of query which i want to change(it has many inner join and group by variables) :
Select RIGHT('0'+LTRIM(DATEDIFF(MINUTE,MIN(CASE WHEN [EnteranceDevice] IN(1)
THEN CAST(EventDate as datetime) ELSE NULL END),(Select Cast([Start] as time)
as Start FROM [dbo].[Period] Where strDay=DATEPART(dw,CAST('2017-01-30' as date))
And Timezone=[dbo].[Timezone].Timezone))/60),2) +':'+
RIGHT('0'+LTRIM(DATEDIFF(MINUTE,MIN(CASE WHEN [EnteranceDevice] IN(1)
THEN CAST(EventDate as datetime) ELSE NULL END),(Select Cast([Start] as time) as Start
FROM [dbo].[Period] Where strDay=DATEPART(dw,CAST('2017-01-30' as date))
And Timezone=[Timezone].Timezone))%60),2) AS WorkingHours from [dbo].[Events]
This query works and it give returns such answer 01:25 hours. When i want to change [EnteranceDevice] IN(1) to [EnteranceDevice] IN(Select ReaderInput from [SPS].[dbo].Users) it gives "Cannot perform an aggregate function on an expression containing an aggregate or a subquery" error
How can i solve this problem?
ReaderInput(nvarchar(50)) has such values(at 1 record) '1,3,5' because of it should store more than 1 device ID but EnteranceDevice is int. So Casting(Cast(EnteranceDevice as nvarchar(50)) IN(...) ) will work?
I'm sure that you see many performance issues at my code. I want to hear recommendations about query.
remember... saying something is "in" is the same same as saying left join and target is not null, so the refactoring is easy as follows:
could not read your code had to reformat it to have some sort of logic....
Select
RIGHT(
'0'+LTRIM(DATEDIFF(MINUTE,MIN(CASE WHEN [EnteranceDevice] IN(1) THEN CAST(EventDate as datetime) ELSE NULL END),
(Select Cast([Start] as time) as Start FROM [dbo].[Period] Where strDay=DATEPART(dw,CAST('2017-01-30' as date)) And Timezone=[dbo].[Timezone].Timezone))/60),
2
) +
':'+
RIGHT(
'0'+LTRIM(DATEDIFF(MINUTE,MIN(CASE WHEN [EnteranceDevice] IN(1) THEN CAST(EventDate as datetime) ELSE NULL END),
(Select Cast([Start] as time) as Start FROM [dbo].[Period] Where strDay=DATEPART(dw,CAST('2017-01-30' as date)) And Timezone=[Timezone].Timezone))%60),
2) AS WorkingHours
from [dbo].[Events]
First of all you have the same sub-query twice. Pull that out into a cross join
Select
RIGHT('0'+LTRIM(DATEDIFF(MINUTE,MIN(CASE WHEN [EnteranceDevice] IN(1) THEN CAST(EventDate as datetime) ELSE NULL END),x.start)/60),2) +
':'+
RIGHT('0'+LTRIM(DATEDIFF(MINUTE,MIN(CASE WHEN [EnteranceDevice] IN(1) THEN CAST(EventDate as datetime) ELSE NULL END),x.start)%60),2) AS WorkingHours
from [dbo].[Events]
cross join (
Select Cast([Start] as time) as Start
FROM [dbo].[Period]
Where strDay=DATEPART(dw,CAST('2017-01-30' as date))
And Timezone=[Timezone].Timezone
) x
Now join to users and check for null
Select
RIGHT('0'+LTRIM(DATEDIFF(MINUTE,MIN(CASE WHEN u.ReaderInput is not null THEN CAST(EventDate as datetime) ELSE NULL END),x.start)/60),2) +
':'+
RIGHT('0'+LTRIM(DATEDIFF(MINUTE,MIN(CASE WHEN u.ReaderInput is not null THEN CAST(EventDate as datetime) ELSE NULL END),x.start)%60),2) AS WorkingHours
from [dbo].[Events]
left join [SPS].[dbo].Users u on [EnteranceDevice] = u.ReaderInput
cross join (
Select Cast([Start] as time) as Start
FROM [dbo].[Period]
Where strDay=DATEPART(dw,CAST('2017-01-30' as date))
And Timezone=[Timezone].Timezone
) x

Display Month Gaps for Each location

I have the following query which takes in the opps and calculates the duration, and revenue for each month. However, for some locations, where there is no data, it is missing some months. Essentially, I would like all months to appear for each of the location and record type. I tried a left outer join on the calendar but that didn't seem to work either.
Here is the query:
;With DateSequence( [Date] ) as
(
Select CAST(#fromdate as DATE) as [Date]
union all
Select CAST(dateadd(day, 1, [Date]) as Date)
from DateSequence
where Date < #todate
)
INSERT INTO CalendarTemp (Date, Day, DayOfWeek, DayOfYear, WeekOfYear, Month, MonthName, Year)
Select
[Date] as [Date],
DATEPART(DAY,[Date]) as [Day],
DATENAME(dw, [Date]) as [DayOfWeek],
DATEPART(DAYOFYEAR,[Date]) as [DayOfYear],
DATEPART(WEEK,[Date]) as [WeekOfYear],
DATEPART(MONTH,[Date]) as [Month],
DATENAME(MONTH,[Date]) as [MonthName],
DATEPART(YEAR,[Date]) as [Year]
from DateSequence option (MaxRecursion 10000)
;
DELETE FROM CalendarTemp WHERE DayOfWeek IN ('Saturday', 'Sunday');
SELECT
AccountId
,AccountName
,Office
,Stage = (CASE WHEN StageName = 'Closed Won' THEN 'Closed Won'
ELSE 'Open'
END)
,Id
,Name
,RecordType= (CASE
WHEN recordtypeid = 'LAS1' THEN 'S'
END)
,Start_Date
,End_Date
,Probability
,Estimated_Revenue_Won = ISNULL(Amount, 0)
,ROW_NUMBER() OVER(PARTITION BY Name ORDER BY Name) AS Row
--,Revenue_Per_Day = CAST(ISNULL(Amount/NULLIF(dbo.CalculateNumberOFWorkDays(Start_Date, End_Date),0),0) as money)
,YEAR(c.Date) as year
,MONTH(c.Date) as Month
,c.MonthName
--, ISNULL(CAST(Sum((Amount)/NULLIF(dbo.CalculateNumberOFWorkDays(Start_Date, End_Date),0)) as money),0) As RevenuePerMonth
FROM SF_Extracted_Opps o
LEFT OUTER JOIN CalendarTemp c on o.Start_Date <= c.Date AND o.End_Date >= c.Date
WHERE
Start_Date <= #todate AND End_Date >= #fromdate
AND Office IN (#Location)
AND recordtypeid IN ('LAS1')
GROUP BY
AccountId
,AccountName
,Office
,(CASE WHEN StageName = 'Closed Won' THEN 'Closed Won'
ELSE 'Open'
END)
,Id
,Name
,(CASE
WHEN recordtypeid = 'LAS1' THEN 'S'
END)
,Amount
--, CAST(ISNULL(Amount/NULLIF(dbo.CalculateNumberOFWorkDays(Start_Date, End_Date),0),0) as money)
,Start_Date
,End_Date
,Probability
,YEAR(c.Date)
,Month(c.Date)
,c.MonthName
,dbo.CalculateNumberOFWorkDays(Start_Date, End_Date)
ORDER BY Office
, (CASE
WHEN recordtypeid = 'LAS1' THEN 'S'
END)
,(CASE WHEN StageName = 'Closed Won' THEN 'Closed Won'
ELSE 'Open'
END)
, [Start_Date], Month(c.Date), AccountName, Row;
I tried adding another left outer join to this and using this a sub query and the join essentially on the calendar based on the year and month, but that did not seem to work either. Suggestions would be extremely appreciated.
--Date Calendar for each location:
;With DateSequence( [Date], Locatio) as
(
Select CAST(#fromdate as DATE) as [Date], oo.Office as location
union all
Select CAST(dateadd(day, 1, [Date]) as Date), oo.Office as location
from DateSequence dts
join Opportunity_offices oo on 1 = 1
where Date < #todate
)
--select result
INSERT INTO CalendarTemp (Location,Date, Day, DayOfWeek, DayOfYear, WeekOfYear, Month, MonthName, Year)
Select
location,
[Date] as [Date],
DATEPART(DAY,[Date]) as [Day],
DATENAME(dw, [Date]) as [DayOfWeek],
DATEPART(DAYOFYEAR,[Date]) as [DayOfYear],
DATEPART(WEEK,[Date]) as [WeekOfYear],
DATEPART(MONTH,[Date]) as [Month],
DATENAME(MONTH,[Date]) as [MonthName],
DATEPART(YEAR,[Date]) as [Year]
from DateSequence option (MaxRecursion 10000)
;
you have your LEFT JOIN backwards if you want all records from CalendarTemp and only those that match from SF_Extracted_Opps then you the CalendarTemp should be the table on the LEFT. You can however switch LEFT JOIN to RIGHT JOIN and it should be fixed. The other issue will be your WHERE statement is using columns from your SF_Extracted_Opps table which will just make that an INNER JOIN again.
here is one way to fix.
SELECT
.....
FROM
CalendarTemp c
LEFT JOIN SF_Extracted_Opps o
ON o.Start_Date <= c.Date AND o.End_Date >= c.Date
AND o.Start_Date <= #todate AND End_Date >= #fromdate
AND o.Office IN (#Location)
AND o.recordtypeid IN ('LAS1')
The other issue you might run into is because you remove weekends from your CalendarTemp Table not all dates are represented I would test with the weekends still in and out and see if you get different results.
this line:
AND o.Start_Date <= #todate AND End_Date >= #fromdate
should not be needed either because you are already limiting the dates from the line before and values in your CalendarTempTable
A note about your CalendarDate table you don't have to go back and delete those records simply add the day of week as a WHERE statement on the select that populates that table.
Edit for All Offices you can use a cross join of your offices table with your CalendarTemp table to do this do it in your final query not the cte that builds the calendar. The problem with doing it in the CTE calendar definition is that it is recursive so you would have to do it in both the anchor and the recursive member definition.
SELECT
.....
FROM
CalendarTemp c
CROSS JOIN Opportunity_offices oo
LEFT JOIN SF_Extracted_Opps o
ON o.Start_Date <= c.Date AND o.End_Date >= c.Date
AND o.Start_Date <= #todate AND End_Date >= #fromdate
AND oo.office = o.Office
AND o.recordtypeid IN ('LAS1')

Group dates by week?

I'm trying to group a series of dates by week. So far I have the following:
SELECT DATEPART(week, CONVERT(VARCHAR(50), e.event_date, 107)) AS 'Date' ,
c.setting_secondary AS 'Workflow Cat' ,
d.setting_main AS 'Error Type' ,
SUM(e.event_count) AS 'Total'
FROM marlin.support_events AS e
INNER JOIN marlin.support_config AS c
ON e.event_category = c.setting_code
AND config_code = 60
INNER JOIN marlin.support_config AS d
ON e.event_type = d.setting_code
AND d.config_code = 70
WHERE e.event_date BETWEEN DATEADD(MONTH, -2, GETDATE()) AND GETDATE()
AND c.setting_secondary = 'Expenditure Voucher'
AND d.setting_main IN ( 'Unstarted' , 'Timeout' )
GROUP BY
DATEPART(week, CONVERT(VARCHAR(50), e.event_date, 107)) ,
c.setting_secondary ,
d.setting_main ,
e.event_summary
This shows me the week number but not the date that week started within, like so:
How can I show what date this week begins with?
Answer:
Answer identified below and an alternate method I also found for doing this:
DATEADD(dd, -(DATEPART(dw, e.event_date)-1), e.event_date)
You can get the year part from the date, append the first day of first month and then add the (#week - 1) to get the starting day of the week the event_date belongs to, as follows:
SELECT EventDate, WorkflowCat, ErrorType, SUM(EventCount) AS 'Total'
FROM
(
SELECT DATEADD(ww,
DATEPART(ww, e.event_date) - 1,
CONVERT(DATETIME,
CONVERT(VARCHAR(4), DATEPART(yy, e.event_date)) + '-01-01')) AS 'EventDate' ,
c.setting_secondary AS 'WorkflowCat' ,
d.setting_main AS 'ErrorType',
e.event_summary as 'EventSummary'
e.event_count AS 'EventCount'
FROM marlin.support_events AS e
INNER JOIN marlin.support_config AS c
ON e.event_category = c.setting_code
AND config_code = 60
INNER JOIN marlin.support_config AS d
ON e.event_type = d.setting_code
AND d.config_code = 70
WHERE e.event_date BETWEEN DATEADD(MONTH, -2, GETDATE()) AND GETDATE()
AND c.setting_secondary = 'Expenditure Voucher'
AND d.setting_main IN ( 'Unstarted' , 'Timeout' )
)
GROUP BY EventDate, WorkflowCat, ErrorType, EventSummary

Sql query to overlay date/period overrides over default dates

Any ideas on building a Sql Server (2008) query that will give me say the "date specific prices for an item based on the default or override where exists".
So a Default table might look like this - columns Price, StartDate, EndDate (yyyy-M-d):
Default: $10, 2010-1-1, 2010-2-1
The Override table like this:
Override: $12, 2010-1-5, 2010-1-8
And the query would return:
Result: $10, 2010-1-1, 2010-1-4
$12, 2010-1-5, 2010-1-8
$10, 2010-1-9, 2010-2-1
Probably I'd wrap this in a stored proc or function and call it for a specific date range.
Soemthing like:
SELECT
D.Price, ISNULL(O.StartDate, D.StartDate), ISNULL(O.EndDate, D.EndDate)
FROM
Default D
LEFT JOIN
Override O ON D.Price= O.Price
The proper design will be to have only one table. You don't want the override table at all. Just keep everyhing in the single table - constrained by the date range. The query becomes much simpler as well.
Your table structure becomes
CREATE TABLE Rates
(ID INT NOT NULL,
Rate Decimal NOT NULL,
FromDate NOT NULL,
ToDate NOT NULL,
CONSTRAINT PK_RATES (ID,FromDate,ToDate))
Then the query becomes
SELECT Rate FROM Rates WHERE ID = #ID AND FromDate = (SELECT MAX(FromDate) FROM Rates WHERE ID = #ID AND FromDate <= #Date) AND ToDate = (SELECT MIN(ToDate) FROM Rates WHERE ID = #ID AND ToDate >=#Date)
I think you're going to have to do something like this (assuming that you want to keep the override dates separate, and that you want to avoid anything procedural):
define and populate a utility table, listing each individual date which could be relevant to you
construct a SELECT that "marks" each date from this utility table as either i) belonging to the override dates or ii) belonging to the default dates
group the results of this SELECT by date and "mark"
join these results back to the respective price info
A bit late, but was looking for a solution for similar problem and did not find an answer so tried to cook one up.
One solution can be to try to adjust the first set of data to fit in the second set, and in the last step we will union the two sets, the adjusted data with the overlay data.
So we need to adjust the default set and that will require several steps of work.
Assumptions:-
The Default periods don’t overlap on their self.
Overlap periods don’t overlap on their self.
The overlap data must have gaps that are more than one day, to ensure that any adjustments will not affect another overlay period; we will not lose the overlay values associated as we will get them back in the last step.
For default periods that are partially overlapped (the overlap is larger or equal to a default period)
If a period start date overlaps with any overlay period we will change the start date to the end of the overlap period plus one day
Default: |<-------- P1 ------>|
Overlap: |<-----------O1------>|
=================================================================
Output: |<-----P1 ---->|
If a period end date overlaps with any overlay period we will change the end date to the start of the overlap period minus one day
Default: |<-------- P1 ------>|
Overlap: |<-----------O1------>|
===================================================================
Output: |<--- P1 ---->|
Remove any default period if it’s completely covered by an overlap period
Default: |<--- P1 --->|
Overlap: |<--------O1------>|
===================================================================
Output: nothing
For default periods that are broken by overlapped periods(the overlap is smaller than the periods)
Get the first part (from the beginning of the period to the first overlap period)
Get the intermediate parts, and that is from the end of any overlapped period to the start of the next overlapped period.
Get the last part( from the last overlapped to the end of the period)
Default: |<---------------------------- P1 ------------------------------>|
Overlap: |<---O1--->| |<---O2-->| |<--O3->|
===================================================================
Output: |<-P1->| |<-P1->| |<-P1->| |<-P1->|
Finally merge the data and get the result
Over lapping types that we are considering
Default: |<---- P1 --->|
Overlap: |<-----------O1------>|
===================================================================
Output: |<---- P1 --->||<-----------O1------>|
Lets build the T-SQL
With [System] as (
Select 1 [RowNum],cast('Jan 01,2017' as date) [StartDate],dateadd(day,-1,cast('Feb 01,2017' as date)) [EndDate],0500 [Value] union all
Select 2 [RowNum],cast('Feb 01,2017' as date) [StartDate],dateadd(day,-1,cast('Mar 01,2017' as date)) [EndDate],0700 [Value] union all
Select 3 [RowNum],cast('Mar 01,2017' as date) [StartDate],dateadd(day,-1,cast('Apr 01,2017' as date)) [EndDate],0900 [Value] union all
Select 4 [RowNum],cast('Apr 01,2017' as date) [StartDate],dateadd(day,-1,cast('May 01,2017' as date)) [EndDate],0700 [Value] union all
Select 5 [RowNum],cast('May 01,2017' as date) [StartDate],dateadd(day,-1,cast('Jun 01,2017' as date)) [EndDate],0900 [Value] union all
Select 6 [RowNum],cast('Jun 01,2017' as date) [StartDate],dateadd(day,-1,cast('Jul 01,9999' as date)) [EndDate],1500 [Value]
),Overrides as (
Select 1 [RowNum],cast('Feb 12,2017' as date) [StartDate],cast('Mar 25,2017' as date) [EndDate],1 [Value] union all
Select 2 [RowNum],cast('Mar 28,2017' as date) [StartDate],cast('May 15,2017' as date) [EndDate],2 [Value] union all
Select 3 [RowNum],cast('May 18,2017' as date) [StartDate],cast('May 20,2017' as date) [EndDate],3 [Value] union all
Select 4 [RowNum],cast('Jun 05,2017' as date) [StartDate],cast('Jun 08,2017' as date) [EndDate],4 [Value] union all
Select 5 [RowNum],cast('Jun 09,2017' as date) [StartDate],cast('Jun 16,2017' as date) [EndDate],5 [Value] union all
Select 6 [RowNum],cast('Jun 17,2017' as date) [StartDate],cast('Jun 22,2017' as date) [EndDate],6 [Value] union all
Select 7 [RowNum],cast('Jun 23,2017' as date) [StartDate],cast('Jun 27,2017' as date) [EndDate],7 [Value]
),PrepareOverridePeriods as (--if override periods have no gabs betwwen we need to merge them
Select p1.StartDate, p1.EndDate
from Overrides p1
left join Overrides p2 on p1.StartDate = DATEADD(day,1,p2.EndDate)
where p2.StartDate is null
union all
select p1.StartDate,p2.EndDate
from PrepareOverridePeriods p1
inner join Overrides p2 on p1.EndDate = DATEADD(day,-1,p2.StartDate)
),OverridePeriods as (
select ROW_NUMBER() over (order by StartDate) [RowNum],StartDate,MAX(EndDate) as EndDate
from PrepareOverridePeriods group by StartDate
),AdjustedPeriods as (
select s.RowNum,'Adj.' [type]
,isnull(dateadd(day,1,ShiftRight.EndDate),s.StartDate) [StartDate]
,isnull(dateadd(day,-1,ShiftLeft.StartDate),s.EndDate) [EndDate]
,s.Value
from System s
left outer join OverridePeriods ShiftRight on s.StartDate between ShiftRight.StartDate and ShiftRight.EndDate
left outer join OverridePeriods ShiftLeft on s.EndDate between ShiftLeft.StartDate and ShiftLeft.EndDate
left outer join OverridePeriods RemovePeriod on s.StartDate between RemovePeriod.StartDate and RemovePeriod.EndDate and s.EndDate between RemovePeriod.StartDate and RemovePeriod.EndDate
where RemovePeriod.StartDate is null
),SmallOverrides as ( --TODO: change SystemCalculated to AdjustSystemCalculatedPeriods
select ROW_NUMBER() over (partition by s.RowNum order by o.StartDate ) [RowNum],
o.RowNum [OverrideRowNum],o.StartDate [OverrideStartDate],o.EndDate [OverrideEndDate],s.Value [Value]
,s.RowNum [SystemRowNum],s.StartDate [SystemStartDate],s.EndDate [SystemEndDate]
from OverridePeriods o
inner join AdjustedPeriods s on o.StartDate between s.StartDate and s.EndDate and o.EndDate between s.StartDate and s.EndDate
)
--,FirstAndLastParts as (
--select [SystemRowNum],[type]
-- ,case when [type]='First' then min([SystemStartDate]) else dateadd(day,1,max(OverrideEndDate)) end [StartDate]
-- ,case when [type]='First' then dateadd(day,-1,min(OverrideStartDate)) else max([SystemEndDate]) end [EndDate]
-- ,min(Value) [Value]
-- from (select *,'First' [type] from SmallOverrides o union all
-- select *,'Last' [type] from SmallOverrides o) data
-- group by [SystemRowNum],[type]
--)
,FirstParts as (
select [SystemRowNum],'First' [type]
,min([SystemStartDate]) [StartDate]
,dateadd(day,-1,min(OverrideStartDate)) [EndDate]
,min(Value) [Value]
from SmallOverrides
group by [SystemRowNum]
),LastParts as (
select [SystemRowNum],'Last' [type]
,dateadd(day,1,max(OverrideEndDate)) [StartDate]
,max([SystemEndDate]) [EndDate]
,min(Value) [Value]
from SmallOverrides
group by [SystemRowNum]
),IntermediatParts as (
select s.SystemRowNum [RowNum],'Inter.' [type]
,dateadd(day,1,s.OverrideEndDate) [StartDate]
,dateadd(day,-1,e.OverrideStartDate) [EndDate]
,s.Value
from SmallOverrides s
left outer join SmallOverrides e on e.SystemRowNum=s.SystemRowNum and s.RowNum+1=e.RowNum
where e.RowNum is not null --remove the first and lasts
),AdjustedPeriodsFiltered as (--remove blocks that are broken to smaller pieces
select s.*
from AdjustedPeriods s
left outer join OverridePeriods o on o.StartDate between s.StartDate and s.EndDate and o.EndDate between s.StartDate and s.EndDate
where o.StartDate is null
),AllParts as (
select * from IntermediatParts union all --order by SystemRowNum,OverrideStartDate
select * from FirstParts union all
select * from LastParts union all
select * from AdjustedPeriodsFiltered
),Merged as (
select [RowNum],[type] [Source],StartDate,EndDate,Value,'System' [RecordType] from AllParts
union all
select [RowNum],'override' [Source],StartDate,EndDate,Value,'Override' [RecordType] from Overrides
)
select * from Merged order by StartDate
Can we adjust the data set in a different way, well yes, another approach is to get all the expected values from the start and end dates for the default periods and the overlay periods, then reconstruct a new set of periods, link it to the default values, merge it with the overlay, and we got it.
The same assumptions and step are taken as below:-
With [System] as (
Select 1 [RowNum],cast('Jan 01,2017' as date) [StartDate],dateadd(day,-1,cast('Feb 01,2017' as date)) [EndDate],0500 [Value] union all
Select 2 [RowNum],cast('Feb 01,2017' as date) [StartDate],dateadd(day,-1,cast('Mar 01,2017' as date)) [EndDate],0700 [Value] union all
Select 3 [RowNum],cast('Mar 01,2017' as date) [StartDate],dateadd(day,-1,cast('Apr 01,2017' as date)) [EndDate],0900 [Value] union all
Select 4 [RowNum],cast('Apr 01,2017' as date) [StartDate],dateadd(day,-1,cast('May 01,2017' as date)) [EndDate],0700 [Value] union all
Select 5 [RowNum],cast('May 01,2017' as date) [StartDate],dateadd(day,-1,cast('Jun 01,2017' as date)) [EndDate],0900 [Value] union all
Select 6 [RowNum],cast('Jun 01,2017' as date) [StartDate],dateadd(day,-1,cast('Jul 01,9999' as date)) [EndDate],1500 [Value]
),Overrides as (
Select 1 [RowNum],cast('Feb 12,2017' as date) [StartDate],cast('Mar 25,2017' as date) [EndDate],1 [Value] union all
Select 2 [RowNum],cast('Mar 28,2017' as date) [StartDate],cast('May 15,2017' as date) [EndDate],2 [Value] union all
Select 3 [RowNum],cast('May 18,2017' as date) [StartDate],cast('May 20,2017' as date) [EndDate],3 [Value] union all
Select 4 [RowNum],cast('Jun 05,2017' as date) [StartDate],cast('Jun 08,2017' as date) [EndDate],4 [Value] union all
Select 5 [RowNum],cast('Jun 09,2017' as date) [StartDate],cast('Jun 16,2017' as date) [EndDate],5 [Value] union all
Select 6 [RowNum],cast('Jun 17,2017' as date) [StartDate],cast('Jun 22,2017' as date) [EndDate],6 [Value] union all
Select 7 [RowNum],cast('Jun 23,2017' as date) [StartDate],cast('Jun 27,2017' as date) [EndDate],7 [Value]
),PrepareOverridePeriods as (--if override periods have no gabs between we need to merge them
Select p1.StartDate, p1.EndDate
from Overrides p1
left join Overrides p2 on p1.StartDate = DATEADD(day,1,p2.EndDate)
where p2.StartDate is null
union all
select p1.StartDate,p2.EndDate
from PrepareOverridePeriods p1
inner join Overrides p2 on p1.EndDate = DATEADD(day,-1,p2.StartDate)
),OverridePeriods as (
select ROW_NUMBER() over (order by StartDate) [RowNum],StartDate,MAX(EndDate) as EndDate
from PrepareOverridePeriods group by StartDate
)
,AllDates as (
select ROW_NUMBER() over (order by [Date]) [RowNum],data.Date from (
select dateadd(day,-1,OverridePeriods.StartDate) [Date] from OverridePeriods union all
select dateadd(day,+1,OverridePeriods.EndDate) [Date] from OverridePeriods union all
select StartDate [Date] from [System] union all
select EndDate [Date] from [System] ) as data
)
,NewPeriods as (
select sy.RowNum, s.[Date] [StartDate],n.[Date] [EndDate] ,sy.Value
from AllDates s
left outer join AllDates n on n.RowNum=s.RowNum+1
left outer join OverridePeriods o on s.[Date] between o.StartDate and o.EndDate and n.[Date] between o.StartDate and o.EndDate
left outer join [System] sy on s.[Date] between sy.StartDate and sy.EndDate
where
s.RowNum % 2 =1 and o.StartDate is null--group it by 2 and remove overriden areas
)
,Merged2 as (
select [RowNum], StartDate,EndDate,Value,'System' [RecordType] from NewPeriods
union all
select [RowNum], StartDate,EndDate,Value,'Override' [RecordType] from Overrides
)
select * from Merged2 order by StartDate
Im sure there may be another way to achieve the result required with some recursive approach, but for now this works for me.
For a last step we can try to merge the results if the value is the same, but I dont think that was requested.