Inconsistent results with CTE (Recursion) - sql

I am pulling my hair with this one!
I have a Stored Procedure that runs everyday:
-- DECLARING DATE VARIABLES
DECLARE #FirstDay date
-- SETTING UP INITIAL VALUES TO DATE VARIABLES
SET #FirstDay = (SELECT MIN(WK_Start) AS FirstDay FROM [TML_RDB].[COMPANY].[COMPANY_CALENDAR]);
-- CREATE DATE RANGE FROM #FIRST DAY
WITH DateRange AS (
SELECT #FirstDay AS [Date]
UNION ALL
SELECT DATEADD("dd",1,[Date]) FROM DateRange
WHERE [Date] < DATEADD("dd",-1,DATEFROMPARTS(YEAR(GETDATE())+2,3,1))
)
SELECT CAST(CONCAT(YEAR(DR.[Date]),FORMAT(MONTH(DR.[Date]),'00'),FORMAT(DAY(DR.[Date]),'00')) AS int) AS [DateKey]
,DR.[Date]
,DATENAME(DW,DR.[Date]) AS [DayOfWeekName]
,CONCAT(RIGHT(YEAR(DR.[Date]),2), FORMAT(DATEPART(WK, DATEADD("dd",-1,DR.[Date])),'00')) AS [CalendarWeek]
,CONCAT('W', RIGHT(YEAR(DR.[Date]),2), FORMAT(DATEPART(WK, DATEADD("dd",-1,DR.[Date])), '00')) AS [CalendarWeekName]
,(DATEPART(DW, DR.[Date])+5) % 7 + 1 AS [DayOfCalendarWeek]
--,DATEPART(WK, DATEADD("dd",-1,DR.[Date])) AS [CalendarWeek]
,MONTH(DR.[Date]) AS [CalendarMonth]
,DATENAME(MM, DR.[Date]) AS [CalendarMonthName]
,DATEPART(DD,DR.[Date]) AS [DayOfCalendarMonth]
,DATEPART(QQ,DR.[Date]) AS [CalendarQuarter]
,CONCAT('Q', DATEPART(QQ,DR.[Date])) AS [CalendarQuarterName]
,ROW_NUMBER() OVER (PARTITION BY YEAR(DR.[Date]), DATEPART(QQ,DR.[Date]) ORDER BY DR.[Date]) AS [DayOfCalendarQuarter]
,CASE
WHEN MONTH(DR.[Date]) < 7 THEN 1
ELSE 2
END AS [CalendarHalf]
,CASE
WHEN MONTH(DR.[Date]) < 7 THEN CONCAT('H',1)
ELSE CONCAT('H',2)
END AS [CalendarHalfName]
,CASE
WHEN YEAR(DR.[Date]) = 2009
THEN ROW_NUMBER() OVER (PARTITION BY YEAR(DR.[Date]), CASE WHEN MONTH(DR.[Date]) < 7 THEN 1 ELSE 2 END ORDER BY YEAR(DR.[Date]), CASE WHEN MONTH(DR.[Date]) < 7 THEN 1 ELSE 2 END) + DATEDIFF(DD,DATEFROMPARTS(YEAR(DR.[Date]),1,1),DATEFROMPARTS(YEAR(DR.[Date]),3,1))
ELSE ROW_NUMBER() OVER (PARTITION BY YEAR(DR.[Date]), CASE WHEN MONTH(DR.[Date]) < 7 THEN 1 ELSE 2 END ORDER BY YEAR(DR.[Date]), CASE WHEN MONTH(DR.[Date]) < 7 THEN 1 ELSE 2 END)
END AS [DayOfCalendarHalf]
,YEAR(DR.[Date]) AS [CalendarYear]
,CONCAT('Y', YEAR(DR.[Date])) AS [CalendarYearName]
,CASE
WHEN YEAR(DR.[Date]) = 2009
THEN ROW_NUMBER() OVER (PARTITION BY YEAR(DR.[Date]) ORDER BY DR.[Date]) + DATEDIFF(DD,DATEFROMPARTS(YEAR(DR.[Date]),1,1),DATEFROMPARTS(YEAR(DR.[Date]),3,1))
ELSE ROW_NUMBER() OVER (PARTITION BY YEAR(DR.[Date]) ORDER BY DR.[Date])
END AS [DayOfCalendarYear]
,CC.[WK_Code] AS [FiscalWeek]
,'W' + CC.[WK_Code] AS [FiscalWeekName]
,DATEPART(DW,DR.[Date]) AS [DayOfFiscalWeek]
,CC.[Sun_Period] AS [FiscalPeriod]
,'P' + CC.[Sun_Period] AS [FiscalPeriodName]
,ROW_NUMBER() OVER (PARTITION BY CC.[Sun_Period] ORDER BY CC.[Sun_Period]) AS [DayOfFiscalPeriod]
,CONCAT(LEFT(CC.[Sun_Period],2), FORMAT(CEILING(((CAST(Right(CC.[Sun_Period],2) AS int)) - 1)/3 ) + 1,'00')) AS [FiscalQuater]
,CONCAT('Q', LEFT(CC.[Sun_Period],2), FORMAT(CEILING(((CAST(Right(CC.[Sun_Period],2) AS int)) - 1)/3 ) + 1,'00')) AS [FiscalQuaterName]
,ROW_NUMBER() OVER (PARTITION BY CONCAT(LEFT(CC.[Sun_Period],2), FORMAT(CEILING(((CAST(Right(CC.[Sun_Period],2) AS int)) - 1)/3 ) + 1,'00')) ORDER BY CONCAT(LEFT(CC.[Sun_Period],2), FORMAT(CEILING(((CAST(Right(CC.[Sun_Period],2) AS int)) - 1)/3 ) + 1,'00'))) AS [DayOfFiscalQuarter]
,CONCAT(LEFT(CC.[Sun_Period],2), FORMAT(CEILING(((CAST(Right(CC.[Sun_Period],2) AS int)) - 1)/6 ) + 1,'00')) AS [FiscalHalf]
,CONCAT('H', LEFT(CC.[Sun_Period],2), FORMAT(CEILING(((CAST(Right(CC.[Sun_Period],2) AS int)) - 1)/6 ) + 1,'00')) AS [FiscalHalfName]
,ROW_NUMBER() OVER (PARTITION BY CONCAT(LEFT(CC.[Sun_Period],2), FORMAT(CEILING(((CAST(Right(CC.[Sun_Period],2) AS int)) - 1)/6 ) + 1,'00')) ORDER BY CONCAT(LEFT(CC.[Sun_Period],2), FORMAT(CEILING(((CAST(Right(CC.[Sun_Period],2) AS int)) - 1)/6 ) + 1,'00'))) AS [DayOfFiscalHalf]
,CONCAT(LEFT(YEAR(DR.[Date]),2), LEFT(CC.[Sun_Period],2)) AS [FiscalYear]
,CONCAT('Y', LEFT(YEAR(DR.[Date]),2), LEFT(CC.[Sun_Period],2)) AS [FiscalYearName]
,ROW_NUMBER() OVER (PARTITION BY CONCAT(LEFT(YEAR(DR.[Date]),2), LEFT(CC.[Sun_Period],2)) ORDER BY CONCAT(LEFT(YEAR(DR.[Date]),2), LEFT(CC.[Sun_Period],2))) AS [DayOfFiscalYear]
FROM DateRange DR
INNER JOIN [TML_RDB].[COMPANY].[COMPANY_CALENDAR] CC
ON DR.[Date] >= CC.[WK_Start] AND DR.[Date] <= CC.[WK_End]
OPTION (MAXRECURSION 10000)
When the above query is run using the Stored Procedure, i get the an output which is incorrect:
This count should start again from 1, not 217.
Now, what is weird is when i run this query manually i get the below result which is correct:
Some important infomration:
This query on some occasions returns the correct results, other times it does not.
The correct result is returned why i reduce the number of rows (Years)
This is to do with the Recursion but cannot undertand it fully.
Any assistance would be much appreciated.
Thanks,

Related

GET current month plus last 13 month SQL

I would like to modify the query which will also include the data from this month.
SELECT TOP (14) Entity = 'Total_Group'
, Scenario = 'Actual'
, Date = TRIM(CAST(YEAR(DATEADD(MONTH, ROW_NUMBER() OVER(ORDER BY object_id) * -1, GETDATE())) as CHAR(4))
+ ' P'
+ CAST(MONTH(DATEADD(MONTH, ROW_NUMBER() OVER(ORDER BY object_id) * -1, GETDATE())) as VARCHAR(2)))
FROM [sys].[all_objects]
the result of this table
starts with 2022P12, but would like to include current month too so it should start with 2023P1
Try this:
SELECT TOP (14) Entity = 'Total_Group'
, Scenario = 'Actual'
, Date = TRIM(CAST(YEAR(DATEADD(MONTH, ROW_NUMBER() OVER(ORDER BY object_id) * -1 + 1, GETDATE())) as CHAR(4))
+ ' P'
+ CAST(MONTH(DATEADD(MONTH, ROW_NUMBER() OVER(ORDER BY object_id) * -1 + 1, GETDATE())) as VARCHAR(2)))
FROM [sys].[all_objects]

How to calculate average of variables in SQL Server

I am trying to calculate date ranges between rows and then get an average.
declare #date0 date = (
select top 1 my_date
from someTable
order by my_date desc)
declare #date1 date = (
select my_date
from someTable
order by my_date desc
offset 1 rows
fetch next 1 row only)
declare #date2 date = (
select my_date
from someTable
order by my_date desc
offset 2 rows
fetch next 1 row only)
declare #date3 date = (
select my_date
from someTable
order by my_date desc
offset 3 rows
fetch next 1 row only)
select
[Range 1] = dateDiff(day, #date1, #date0),
[Range 2] = dateDiff(day, #date2, #date1),
[Range 3] = dateDiff(day, #date3, #date2),
[Avg Range] = avg(
nullIf(#date0, 0),
nullIf(#date1, 0),
nullIf(#date2, 0),
nullIf(#date3, 0)
)
The range calculations work fine, but a bit clumsy.
However, I'm not sure how to handle the average. It looks like the function is supposed to run against a table and not an array, but I was having issues inserting the variables into a temp table column.
How can I get the average of these ranges (not including range = 0)?
Use UNION ALL to return a row for each case:
select avg(t.[Range]) [Avg Range]
from (
select dateDiff(day, #date1, #date0) [Range]
union all
select dateDiff(day, #date2, #date1)
union all
select dateDiff(day, #date3, #date2)
) t
where t.[Range] <> 0
AVG is an aggregate function, designed to be used with GROUP BY or windows. You can simply do the math in your query:
select
[Range 1] = dateDiff(day, #date1, #date0),
[Range 2] = dateDiff(day, #date2, #date1),
[Range 3] = dateDiff(day, #date3, #date2),
[Avg Range] = (
nullIf(#date0, 0) +
nullIf(#date1, 0) +
nullIf(#date2, 0) +
nullIf(#date3, 0)
) /
(
CASE WHEN #date0 IS NULL THEN 0 ELSE 1 END +
CASE WHEN #date1 IS NULL THEN 0 ELSE 1 END +
CASE WHEN #date2 IS NULL THEN 0 ELSE 1 END +
CASE WHEN #date3 IS NULL THEN 0 ELSE 1 END
)
No reason to use four different queries:
with dates as (
select
row_number() over (order by my_date desc) rn,
datediff(days, lag(my_date) over (order by my_date desc), my_date) diff
from T
)
select avg(nullif(diff, 0)) from dates where rn <= 3;
or
with dates as (
select
datediff(days, lag(my_date) over (order by my_date desc), my_date) diff
from T
order by my_date desc
fetch next three rows only
)
select avg(nullif(diff, 0)) from dates;
Using distinct would also let you easily get the top three dates and not have to mess around with nullif().
with dates as (
select distinct
datediff(days, lag(my_date) over (order by my_date desc), my_date) diff
from T
order by my_date desc
fetch next three rows only
)
select avg(diff) from dates;

SQL case statement closest to current date

Need help create a case statement to find the closest date from date table. My data: https://imgur.com/hkBu4SA
I basically want to set:
Y flag if it's closest to today's date from a.FROM_EFFDT and is not null.
F if to_effdate is null
else N
WHEN a.FROM_EFFDT < GETDATE() AND (to_effdate) IS NOT NULL THEN 'Y'
WHEN to_effdate IS NULL THEN 'F'
ELSE 'N'
You can use window functions:
(case when row_number() over (order by abs(datediff(day, getdate(), to_effdate)) = 1
then 'Y'
when to_effdate is null then 'F'
else 'N'
end)
You may be able to accomplish it with something like this. Though this isn't bulletproof, you could get duplicates if the closest date is tied.
create table Dates (from_effdt datetime, to_effdt datetime, flag varchar(1))
insert Dates (from_effdt, to_effdt, flag)
values
('2019-03-16', null, '') ,
('2018-06-14', '2019-03-16', '') ,
('2018-05-14', '2018-06-14', '') ,
('2018-01-01', '2018-05-14', '')
select * from Dates
UPDATE Dates
SET flag =
CASE
WHEN from_effdt = (
select top 1 from_effdt
from Dates
order by ABS ( DATEDIFF(day, from_effdt, getdate()) )
)
THEN 'Y'
ELSE
'N'
END
*update, not sure why I created it as an update. This select should do.
SELECT from_effdt, to_effdt,
CASE
WHEN from_effdt = (
select top 1 from_effdt
from Dates
order by ABS ( DATEDIFF(day, from_effdt, getdate()) )
)
THEN 'Y'
ELSE
'N'
END [numberOfDaysAway]
FROM Dates
You can simply do this:
CASE
WHEN from_effdt = (
select from_effdt
from Dates
where abs(datediff(second, from_effdt, getdate()))
= (select min(
abs(datediff(second, from_effdt, getdate()))
)
from Dates)
)
THEN 'Y'
ELSE
'N'
END
ROW_NUMBER() Over (Partition by id order by to_effdt desc)
,id
,from_effdt
,to_effdt
, CASE WHEN (ROW_NUMBER() Over (Partition by id order by to_effdt desc) = 1) THEN ('Y')
WHEN (to_effdt IS NULL) THEN ('F') ELSE ('N') End as flag
from a

Iterate value dynamically

I'm using the below query to calculate a budget value dynamically means iterating upto selected date value.
SUM(case when Name = 'Budget' then Value + ((Value/#TotaldaysinMonth) *
#DaysPastinMonth) end) as [Budget]
Here variable #DaysPastinMonth should be dynamic. Means if I select a date as 03/31/2017. Then the query should run upto the previous month value. Another example is if I select August, then I need to run query from Jan-Aug.
For Jan
SUM(case when Name = 'Budget' then Value + ((Value/#TotaldaysinMonth) *
#DaysPastinJanMonth) end) as [Budget]
For Feb
SUM(case when Name = 'Budget' then Value + ((Value/#TotaldaysinMonth) *
#DaysPastinFebMonth) end) as [Budget]
For Mar
SUM(case when Name = 'Budget' then Value + ((Value/#TotaldaysinMonth) *
#DaysPastinMarMonth) end) as [Budget]
Also I have created variables for all the 12 months which holds DaysPastinMonth.
Can anyone suggest how this can be achieved using case statement.
You are thinking about this in loop when you could do it with set based operations.
----------------------------------------------------------
--Create a table of dates for testing
----------------------------------------------------------
if object_id('tempdb..#dates') is not null
drop table #dates
create table #dates(d date
,RN bigint)
declare #sdate datetime='2017-01-01 00:00'
declare #edate datetime='2017-7-31 00:00'
insert into #dates
select
DATEADD(d,number,#sdate)
,row_number() over (order by (select null)) as RN
from
master..spt_values
where
type='P'
and number<=datediff(d,#sdate,#edate)
declare #numOfDays int = (select count(*) from #dates)
----------------------------------------------------------
--Populate Test Data
----------------------------------------------------------
if object_id('tempdb..#testTable') is not null
drop table #testTable
create table #testTable([Name] varchar(64),
[Value] decimal (16,4),
DT datetime)
insert into #testTable ([Name],[Value],DT)
select
'Budget'
,r.randomNumber
,d.d
from
#dates d
inner join
(SELECT TOP (select #numOfDays)
randomNumber,
row_number() over (order by (select null)) as RN
FROM (
SELECT CAST(ABS(CAST(NEWID() AS binary(6)) %100000) + RAND() AS DECIMAL (16,4)) + 1 randomNumber
FROM sysobjects) sample
GROUP BY randomNumber
ORDER BY randomNumber DESC) r on r.RN = d.RN
union all
select
'Not The Budget'
,r.randomNumber
,d.d
from
#dates d
inner join
(SELECT TOP (select #numOfDays)
randomNumber,
row_number() over (order by (select null)) as RN
FROM (
SELECT CAST(ABS(CAST(NEWID() AS binary(6)) %100000) + RAND() AS DECIMAL (16,4)) + 1 randomNumber
FROM sysobjects) sample
GROUP BY randomNumber
ORDER BY randomNumber DESC) r on r.RN = d.RN
----------------------------------------------------------
--Instead of making your variables "dynamic" which
--would likely consist of some loop, just pass in the
--month you care about and let SQL do the work
----------------------------------------------------------
declare #month datetime = '2016-03-31'
select
DT
,[Value]
,[Name]
,sum(case when [Name] = 'Budget'
then [Value] +
(([Value] / (DATEDIFF(day,DATEADD(month, DATEDIFF(month, 0, #month), 0),#month)))
*
(DATEDIFF(DAY,DATEADD(MONTH, DATEDIFF(MONTH, 0, #month)-1, 0),DATEADD(MONTH, DATEDIFF(MONTH, -1, #month)-1, -1)))) end) as Budget
from
#testTable
where
DT >= DATEADD(yy, DATEDIFF(yy, 0, #month), 0) --this is Jan 1 of the year associated with your vairable
group by
DT
,[Name]
,[Value]

How to display event hour by hour in SQL?

Create table tblEvent ( Event_ID int, Start_Time datetime, End_Time datetime )
insert into tblEvent values(1,'2015-02-10 9:00:00.000','2015-02-10 11:00:00.000')
insert into tblEvent values(2,'2015-02-10 11:00:00.000','2015-02-10 11:20:00.000')
insert into tblEvent values(3,'2015-02-10 11:20:00.000','2015-02-10 13:00:00.000')
and want to be display like below
Hour Event_ID [Start_End]
9 1 9:00-10:00
10 1 10:00-11:00
11 2 11:00-11:20
11 3 11:20-12:00
12 3 12:00-13:00
and we can make the End_Time of Event 3 become 13:30
we had to be display
13 3 13:00-13:30
Can anyone help me?
You can use DATEPART function
DATEPART(HOUR, [Start_End]) AS Hour
select blocks.Hour, e.Event_Id,
format(case when e.Start_Time > blocks.Start_Time then e.Start_Time else blocks.Start_Time end, 'HH:mm') +
'-' +
format(case when e.End_Time < blocks.End_Time then e.End_Time else blocks.End_Time end, 'HH:mm')
from
tblEvent as e inner join
(
select
d0.n + d1.n * 4 as Hour,
dateadd(hh, d0.n + d1.n * 4, cast(cast(current_timestamp as date) as datetime)) as Start_Time,
dateadd(hh, d0.n + d1.n * 4 + 1, cast(cast(current_timestamp as date) as datetime)) as End_Time
from
(select 0 as n union all select 1 union all select 2 union all select 3) as d0,
(select 0 as n union all select 1 union all select 2 union all select 3 union all select 4 union all select 5) as d1
) as blocks
on blocks.End_Time > e.Start_Time and blocks.Start_Time < e.End_Time
order by Event_Id, Hour
Here's a start. SQL Server? Is current day enough? You don't have the format() on SQL 2008 so you'll have to do that part yourself.
I'm not sure this handles all the cases exactly the way you want. You can take the basic idea and extend it across a longer range of hours, say 168 for a full week.
http://sqlfiddle.com/#!6/819c0/9
TRY Some thing like this.This sample data is running ok.
Please provide another sample data atleast 10 rows and don't forget to paste desired output.
Also read my comment in script.
DECLARE #tblEvent TABLE (
Event_ID INT
,Start_Time DATETIME
,End_Time DATETIME
)
INSERT INTO #tblEvent
VALUES (
1
,'2015-02-10 9:00:00.000'
,'2015-02-10 11:00:00.000'
)
,(
2
,'2015-02-10 11:00:00.000'
,'2015-02-10 11:20:00.000'
)
,(
3
,'2015-02-10 11:20:00.000'
,'2015-02-10 13:00:00.000'
);
--select *,DATEdiff(hour,a.Start_Time,a.End_Time) from #tblEvent a
;
WITH CTE
AS (
SELECT *
,ROW_NUMBER() OVER (
ORDER BY Start_Time
) RN
,DATEdiff(hour, Start_Time, End_Time) Diff
FROM #tblEvent
)
--select * from cte
,CTE1
AS (
SELECT Event_ID
,Start_Time
,CASE
WHEN Diff > 1
THEN DATEADD(minute, 60 - DATEPART(minute, Start_Time), Start_Time)
ELSE End_Time
END End_Time
,RN
,DIFF
,1 RN1
,DATEPART(minute, Start_Time) DIFFMIN
FROM CTE
--WHERE RN = 1
UNION ALL
SELECT CASE
WHEN A.Diff > B.DIFF
THEN b.Event_ID
ELSE a.Event_ID
END
,B.End_Time Start_Time
,CASE
WHEN A.Diff > B.DIFF
THEN DATEADD(minute, 60 - DATEPART(minute, B.Start_Time), B.End_Time)
ELSE A.End_Time
END End_Time
,CASE
WHEN A.Diff > B.DIFF
THEN B.RN
ELSE B.RN + 1
END RN
,CASE
WHEN A.Diff > B.DIFF
THEN B.DIFF - 1
ELSE A.Diff
END
,RN1 + 1
,0
FROM CTE1 B
CROSS APPLY (
SELECT *
FROM CTE
WHERE RN = B.RN
) A
WHERE B.DIFF > 0
)
SELECT [Hour]
,Event_ID
,[Start_End]
FROM (
SELECT DATEPART(HOUR, Start_Time) [Hour]
,ROW_NUMBER() OVER (
PARTITION BY Start_Time ORDER BY Start_Time
) RN2
,Event_ID
,CONVERT(VARCHAR(5), Start_Time, 114) + '-' + CONVERT(VARCHAR(5), End_Time, 114) [Start_End]
FROM CTE1
) TBL
WHERE RN2 = 1
--BELOW QUERY RETURN 6 ROWS
-- I AM TRYING TO ELIMINATE THE EXTRA ROWS WITHOUT ROW_NUMBER
--WHICH WOULD BE MORE OPTIMIZE,BUT I AM NOT GETTING WHAT ACTUALLY CAUSING THIS BEHAVIOUR
--MEANWHILE YOU CAN TEST OTHER SAMPLE DATA,AND THROW OTHER SAMPLE DATA
--SELECT DATEPART(HOUR, Start_Time) [Hour]
-- ,Event_ID
-- ,CONVERT(VARCHAR(5), Start_Time, 114) + '-' + CONVERT(VARCHAR(5), End_Time, 114) [Start_End]
-- FROM CTE1