Recurring tasks using SSIS - sql

I created a new SSIS package (SQL 2017) that reads through an "Export Request" table and exports data based on the schedule to an external storage. For example, users can add a new row to this table like "select * from table 1 where col1=2" with the schedule as June 25, 2018 3:30 pm.
My package (which is running using SQL agent) loops all the time in search of new tasks in that table and when scheduled, exports the result of the queries to a folder at that time.
There is a new feature requested to have these exported scheduled for recurring. For example, customers might want to export every day starting June 25, 2018 3:30 pm. The schedule can get complicated for annually, monthly, daily, every 3 month, ...
What is the best way to implement recurring inside SSIS? I can use a 5 char field for Cron but don't know how to query for new tasks using their Cron schedule.
Any help?

To confirm what you already have: You have a TASK table with tasks in it and some sort of date flag for when it needs to run. Then your job runs every x number of minutes and checks if there are any tasks to be picked up/run. If so it runs them. Then i am guessing you have a flag in the table you set saying it is completed after you run it in your loop? If you have multiple tasks to run at a time it just runs them all one at a time?
Then what you want to do is add this to it: something I did something very similar. I created a lookup table for each task (in my process each task had a name and I used that name to reference the lookup table, I called it schedule lookup).
In that table I put when and how often the report would need to run (so 2 columns) time of day and frequency. One example is 7:00 and weekdays. So for this report it runs every weekday only (m-F) and at 7pm.
Then when my job runs it would run the task like you have above, but then have another step, that would flag that task as complete, but then insert a new task in the task table (the task details would be the same) but I would look at my schedule lookup table described above to figure out the next date/time when the job should run again and use that as my next run date/time in my task table.
Below is the SP I use in my process to get the next schedule day/time and to update the existing one to completed, and then to create the new one.
NOTE: My scheduling has some advanced options you may not need, I have LOTS of comments that should explain what/why I am doing everything. I am calling a few functions I created in places but I dont think you would need those and can figure out what to do instead of my functions, but if you have questions let me know:
This is what I used so it is using my table structures/etc, but you could convert it to yours easily enough.
--Purpose
----------------------------------------------------------------------------------
-- calculates the next time to run/schedule the job
----------------------------------------------------------------------------------
-- NOTE: TO USE you have to insert the first value manually in queue table
----- possibile scenerios
-- if we want to schedule every x hours, or x days
-- run every month only
-- run weekdays only
-- run on certain days of month only
-- TO ADD MORE COMPLEX or different types of schedules:
-- special - different times for different days of week
-- ex - so have dayofweek:2:00,dayofweek:3:00 (and we parse out the day of week and number splitting out the strings)
-- hourly - to do more then once a day??
-- WHEN #ScheduleLookupType = 'hourly' THEN DATEADD(DAY, 1, #CurrentScheduleDate) -- FIX FIX FIX
-- EXEC dbo.JobsDynamicRescheduleFindNextTimeToScheduleJob #ReportName = 'TestReport1'
----------------------------------------------------------------------------------
ALTER PROCEDURE [dbo].[JobsDynamicRescheduleFindNextTimeToScheduleJob]
#ReportName VARCHAR(50)
AS
SET NOCOUNT ON
BEGIN TRY
-- left here for testing outside of SP
-- this will be passed from SP
--DECLARE #ReportName AS VARCHAR(50)
--SET #ReportName = 'TESTREport'
-- this sets the first day of the week to Monday (we need it set to a value to do calcluations for weekdays, I set it to 1 for monday and 7 for sunday)
-- this is due to server settings could have somethign else so forcing it here
SET DATEFIRST 1
DECLARE #CurrentScheduleDate AS DATE -- find the current date for the job that just ran
DECLARE #CurrentScheduleDayNumberOfWeek AS SMALLINT -- this pulls the number of the day of week 1=monday, 2=tuesdday
DECLARE #ScheduleLookupType AS VARCHAR(20) -- this is the type of schedule to do calculations on
DECLARE #TimeOfDayToScheduleJob AS VARCHAR(20) -- look this up, its the time to schedule the job
DECLARE #SpecialScheduleValue AS VARCHAR(8000) -- this is special value to lookup (only needed if non standard one)
DECLARE #NewScheduleDateONLY AS DATETIME -- to hold just the date of the schedule before combinng with time
DECLARE #NewScheduleDateTime AS DATETIME -- to hold the new schedule date and time, actual value to insert into queue
-- pull the current schedule date/time from the queue table
SELECT #CurrentScheduleDate = NextRunDateTime
FROM dbo.GenericReportingQueue (NOLOCK)
WHERE IsGenerated IS NULL
AND ReportName = #ReportName
-- to override for testing
--SET #CurrentScheduleDate = '5/20/2016'
SET #CurrentScheduleDayNumberOfWeek = DATEPART(WEEKDAY, #CurrentScheduleDate)
-- pull these values from lookup table
SELECT #ScheduleLookupType = dbo.fn_GetValueLookupTableValue(#ReportName, 'RescheduleJobDynamic_ScheduleLookupType'),
#TimeOfDayToScheduleJob = dbo.fn_GetValueLookupTableValue(#ReportName, 'RescheduleJobDynamic_TimeOfDayToScheduleJob'),
#SpecialScheduleValue = dbo.fn_GetValueLookupTableValue(#ReportName, 'RescheduleJobDynamic_SpecialScheduleValue')
/*
-- reset for testing
SET #ScheduleLookupType = 'specialdays' -- weekly, weekdays, monthly, specialdays
SET #TimeOfDayToScheduleJob = '8:00'
SET #SpecialScheduleValue = '5,6'
*/
-- calculations to get the date to schedule the job next time based off logic
SELECT #NewScheduleDateONLY = CASE
WHEN #ScheduleLookupType = 'daily' THEN DATEADD(DAY, 1, #CurrentScheduleDate)
WHEN #ScheduleLookupType = 'weekly' THEN DATEADD(DAY, 7, #CurrentScheduleDate)
WHEN #ScheduleLookupType = 'monthly' THEN DATEADD(MONTH, 1, #CurrentScheduleDate)
WHEN #ScheduleLookupType = 'yearly' THEN DATEADD(YEAR, 1, #CurrentScheduleDate)
-- only run on weekdays and skip weekends
WHEN #ScheduleLookupType = 'weekdays' THEN
CASE
WHEN #CurrentScheduleDayNumberOfWeek IN (1, 2, 3, 4) THEN DATEADD(DAY, 1, #CurrentScheduleDate)
WHEN #CurrentScheduleDayNumberOfWeek = 5 THEN DATEADD(DAY, 3, #CurrentScheduleDate)
END -- end case for day of week
-- only run on weekends and skip weekdays
WHEN #ScheduleLookupType = 'weekends' THEN
CASE
WHEN #CurrentScheduleDayNumberOfWeek = 6 THEN DATEADD(DAY, 1, #CurrentScheduleDate)
WHEN #CurrentScheduleDayNumberOfWeek = 7 THEN DATEADD(DAY, 6, #CurrentScheduleDate)
END -- end case for weekends only
WHEN #ScheduleLookupType = 'specialdays' THEN
-- for this we need to determine the current day, and the next day we want to run on, then add that many days
-- if next day is not till the following week we just find the first day in the list
-- Take taht number and do dateadd to it
DATEADD(DAY,
-- this does the select to determine what number to add based off current day and next day list
(SELECT ISNULL(
-- if this one I want to take today value and subtract from next value found
-- then add that number to todays date to give me the next schedule date
(SELECT TOP 1 StringValue - #CurrentScheduleDayNumberOfWeek
FROM dbo.fn_ParseText2Table(#SpecialScheduleValue, ',')
WHERE StringValue > #CurrentScheduleDayNumberOfWeek
ORDER BY StringValue)
,
-- if none found above I need to go to the next weeks first value
-- I need to take 7 - todays number (to get the rest of the week) then add the next number for the next week to it
(SELECT TOP 1 (7 - #CurrentScheduleDayNumberOfWeek) + StringValue
FROM dbo.fn_ParseText2Table(#SpecialScheduleValue, ',')
ORDER BY StringValue)
)-- end is null
) -- end select
, #CurrentScheduleDate) -- end dateadd for speical days
END -- outer case
SET #NewScheduleDateTime = #NewScheduleDateONLY + ' ' + #TimeOfDayToScheduleJob
-- for testing
--SELECT #ScheduleLookupType AS ReportLookupType, #TimeOfDayToScheduleJob AS TimeOfDayToSchedule, #SpecialScheduleValue AS SpecialValuesForCalc, #NewScheduleDateTime AS NewDateTimeToRun,
--#CurrentScheduleDate AS CurrentDateSchedule, #CurrentScheduleDayNumberOfWeek AS CurrentNumberDayOfWeek, #NewScheduleDateONLY AS NewScheduleDateOnly
-- &&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&
-- now update and insert the new schedule date/time into the table
-- update existing record
UPDATE dbo.GenericReportingQueue
SET IsGenerated = 1,
DateReportRun = GETDATE(),
LastUpdateDate = GETDATE()
WHERE ISGenerated IS NULL
AND ReportName = #ReportName
-- insert new record with new date
INSERT INTO dbo.GenericReportingQueue (
ReportName, NextRunDateTime, CreatorID, CreateDate
)
SELECT #ReportName, #NewScheduleDateTime, 1, GETDATE()
END TRY
BEGIN CATCH
RETURN
END CATCH

Related

Dynamic Date Variable

Is there a way to cycle through consecutive data values for a variable in my code?
My date variable updates every Saturday, so whenever I update my table I have to manually backdate the code by changing the data variable every execution. This is a very lengthy process as I am backdating 30+ dates with a code that takes 1-2 minutes to run.
For example, if I loaded a secondary table with the date of every Saturday in 2020-2021 (for now), could I cycle the variable through the values?
Declare #Saturday Date
Set #Saturday = '2021-04-24'
Declare #Friday Date
Set #Friday = '2021-04-30'
;
I had a similar thing where I had to recalculate values per day and if some configuration changed we had to backdate those changes per day for x number of days
I used something like this to achieve the result, essentially starting at the furtherst point back and moving forwards but you can run it the other way if that is preferable. On the other hand the code I was running would take a few seconds per execution rather than 1-2 minutes.
Declare #Saturday Date
Set #Saturday = '2020-01-04';
while #Saturday < GETDATE()
begin
print #saturday; /*do something*/
set #Saturday = DATEADD(WEEK, 1, #Saturday);
end

SQL query that displays the current date and count of days between two specific dates

I am trying to write a SQL query that shows the count of the days and date depending upon the financial period it falls in. The financial period starts 5 days before the month end eg march 27th to 26th April
For the above mentioned period if the day is 29th march, the count of the day should be 3 and the date should be 2020-04. The date should adjust depending upon the period it falls in.
I tried to adress the second part of this query by writing the below script but it does not bring any result
declare #date datetime
set #date = getdate()
SELECT format (date,'yyyy-MM-dd') as date
where #date
between dateadd(day,-5,EOMONTH(getdate(),-1)) and dateadd(day,-5,EOMONTH(getdate()))
updated to include 5 days from previous month.
Is this what you are looking for the second part?
Edit: Modifying the query. I guess this should give you what you need for both the parts.
you can try changing the dates in set #date.
Please note that the -4 instead of -5 is done intentionally as you said the financial month starts 5 days earlier. For March, 31 - 5 would give 26, but it should start on 27 right? so that on 29th the no. of days should be 3 including 27 and 29. Anyways, the query should be self explanatory, might just need to change the number depending on your requirement.
declare #date datetime
--set #date = getdate()
set #date = '2020-02-14'
SELECT format(#date,'yyyy-MM-dd') as date,
case WHEN #date < dateadd(day,-4,EOMONTH(#date)) THEN format(EOMONTH(#date),'yyyy-MM')
ELSE format(EOMONTH(#date, 1),'yyyy-MM') END
AS FinancialMonth,
CASE WHEN #date < dateadd(day,-4,EOMONTH(#date)) THEN DATEDIFF(day,dateadd(day,-5,EOMONTH(#date,-1)), #date)
ELSE DATEDIFF(day, dateadd(day,-5,EOMONTH(#date)), #date) END
AS CountDays
The following code will calculate what you need. Please read the comments in the code itself.
-- First, lets declare all the variables we need.
-- I tried to name them so they are selfexplanatory.
declare #inputdate DateTime,
#financialPeriodStartDate DateTime,
#EndOfTheMonth DateTime,
#nextFinantialMonthYear DateTime,
#previousFinancialPeriodStartDate DateTime
-- Now we calculate some of the values
set #inputdate = GETDATE() -- Let's use Today's date, but you can change this to any date.
set #EndOfTheMonth = EOMONTH(#inputdate) -- This is the end of the month for the input date
set #financialPeriodStartDate = DATEADD(DAY,-5,EOMONTH(#inputdate)) -- This is you Financial Period Start Date for the giving input date.
set #previousFinancialPeriodStartDate = DATEADD(DAY,-5,EOMONTH(DATEADD(MONTH, -1, #inputdate))) -- Also calculates the Start Date of the precious financial period.
set #nextFinantialMonthYear = DATEADD(MONTH, 1,#financialPeriodStartDate) -- This is the start date of the next financial period.
-- In the following Select, it calculates the values you need, based in the previous variables.
select
#inputdate as Today,
#EndOfTheMonth as EndOfMonth,
#previousFinancialPeriodStartDate as PreviousFinancialPeriodStartDate,
#financialPeriodStartDate as FinacialPeriodStartDate,
CASE WHEN DATEDIFF(DAY , #financialPeriodStartDate, #inputdate + 1 ) < 0
THEN DATEDIFF(DAY , #previousFinancialPeriodStartDate, #inputdate + 1 )
WHEN DATEDIFF(DAY , #financialPeriodStartDate, #inputdate + 1 ) >=0
THEN DATEDIFF(DAY , #financialPeriodStartDate, #inputdate + 1 )
END as DaysFromFPStartDate,
STR(YEAR(#nextFinantialMonthYear)) +'-'+LTRIM(RTRIM(STR(MONTH(#nextFinantialMonthYear)))) as NextFinantialPeriodMonthAndYear
I hope it helps.

Public holidays that fall on a sunday

I dont know about other countries but in South Africa we have a public holiday on a Monday if the Sunday was a Public holiday. I need to write an Update statement that will return the date after x number of days and should the date be a monday after a public holiday it should add an additional day. My simple update statement looks like:
UPDATE tbl_ProjectTracker
set Predicted_Date = DATEADD(DAY, 20, Actual_Eng_Start_date)
I already have a table for referencing but I am not sure if I should determine the additional day on the table or on the Update statement.
Please assist
You should have a PublicHoliday table
Then you could Update predicted_date like that
DECLARE #PublicHoliday AS TABLE
(
HolidayDate date
)
DECLARE #NumberDaysPredict int = 20
UPDATE pt
set pt.Predicted_Date = DATEADD(DAY, #NumberDaysPredict + hl.NumberHolidaysOnSunday, pt.Actual_Eng_Start_date)
FROM tbl_ProjectTracker pt
CROSS APPLY
(
SELECT Count(*) AS NumberHolidaysOnSunday
FROM #PublicHoliday ph
WHERE ph.HolidayDate BETWEEN pt.Actual_Eng_Start_date AND DATEADD(DAY, #NumberDaysPredict, Actual_Eng_Start_date)
AND Datepart(dw,ph.HolidayDate) = 1 -- Sunday
) hl
A simple way would be a case construct for the number of days:
UPDATE tbl_ProjectTracker
set Predicted_Date = DATEADD(
DAY,
CASE WHEN Actual_Eng_Start_date IN
(select day from public_holidays where datepart(dw, day) = 1) THEN 21 ELSE 20 END,
Actual_Eng_Start_date)
Unfortunately SQL Server's date / time functions are weak to say the least. The result of DATEPART(dw, ...) depends on a setting (DATEFIRST), so the query is kind of unreliable. Always make sure the setting is correct when you run it. It has been requested to extend DATEPART such as to accept an optional parameter for DATEFIRST so you'd get a consistent query, but Microsoft has closed this as "unsolvable" (https://connect.microsoft.com/SQLServer/feedbackdetail/view/432303/datepart-dw-date-should-allow-optional-date-first-Parameter).

sql custom generated week ranges

I'm trying to create custom week numbers and ranges based on user entered Week Start Day.
I'm passing that User defined day with SET Datefirst = #Userdefinedvalue (using a function to map my weekday start values to T-SQL's). Also I have a function to gets me the last day of the current week.
What is the best approach for this?
My Goal is: if I select Tuesday as my start day of the week the Stored Procedure to generate my week numbers and start/end dates based on my selection for the entire year
The idea is to find the first day of the week. First, use SQL Server built in functions to move the start of the week to the Sunday (in the US) by subtracting datepart(wd). Actually datepart(wd) returns values form 1 to 7 and we want them 0 to 6.
Then, add back in an offset based on your date of the week. Here is example code:
declare #offset int (case when #DateFirst = 'Mon' then 1
when #DateFirst = 'Tue' then 2
...
when #DateFirst = 'Sun' then 0
end);
select dateadd(dd, #offset - (datepart(wd, thedate) - 1)
thedate) as WeekStartDate

SQL Recur every x weekday every x weeks

I'm trying to write a sql query which depending on what the user selects will recur every x day every x weeks.
So the user will select that they want the job to recur on tuesdays every 2 weeks
the values that are supplied are
declare #StartDate datetime -- when the job first recurs
declare #recurrenceValue1 int -- amount of weeks
declare #recurrenceValue2 int -- day of week (mon-sun)
declare #NextOcurrance datetime -- when the job will recur
I know how to set it for the amount of weeks:
SET #NextOccurance = (Convert(char(12),#StartDate + (#RecurrenceValue1),106))
But I'm not sure how to make it roll on to the day of the week so if the #startDate is today and it should recur every 2 weeks on a tuesday it will see that 2 weeks today is wednesday so will loop until it know that the day is tuesday and that will be the #NextRecurrance date.
Thanks in advance
An easy way to add a number of weeks to a date is to use (MSDN DATEADD)
DATEADD(wk, #StartDate, #recurrenceValue1)
To find out which day of the week the date you are looking at belongs, you can use (MSDN DATEPART)
DATEPART(dw, #StartDate)
This function uses DATEFIRST to determine which day of the week is the first one (http://msdn.microsoft.com/en-us/library/ms181598.aspx)
SET DATEFIRST 1 --Where 1 = Monday and 7 = Sunday
So for your problem (DATEFIRST being set to 1 = Monday)..
SET DATEFIRST 1
declare #StartDate datetime -- when the job first recurs
declare #recurrenceValue1 int -- amount of weeks
declare #recurrenceValue2 int -- day of week (mon-sun)
declare #NextOcurrance datetime -- when the job will recur
SET #StartDate = '2011-12-16' -- This is a Friday
SET #recurrenceValue1 = 2 -- In 2 weeks
SET #recurrenceValue2 = 2 -- On Tuesday
SET #NextOcurrance = DATEADD(wk, #recurrenceValue1, #StartDate) -- Add our 2 weeks
/* Check if our incrementation falls on the correct day - Adjust if needed */
IF (DATEPART(dw, #NextOcurrance) != #recurrenceValue2) BEGIN
DECLARE #weekDay int = DATEPART(dw, #NextOcurrance)
SET #NextOcurrance = DATEADD(dd, ((7 - #weekDay) + #recurrenceValue2), #NextOcurrance) -- Add to #NextOcurrance the number of days missing to be on the requested day of week
END
The logic for the number of days to add is as follow:
We have 7 days in a week, how many days does it take to reach the end of this week. Add this number of days to the #recurrenceValue2 (day of week we are looking for).
PS: I can't post more than 2 HyperLinks because of my reputation. This is why the DATEFIRST URL is in plain text.
Here's some code to allow certain specific date to be handled differently. Though this code is good only for unique dates. If ones needs to skip an entire week for instance, using this code will require to add values for each day of this week to skip. For ranges other than unique days, this code should be modified to handle date ranges or specific weeks and/or day of week.
CREATE TABLE OccurenceExclusions (
ExclusionDate DATE not null,
NumberOfDaysToAdd int not null
PRIMARY KEY (ExclusionDate)
)
INSERT OccurenceExclusions VALUES ('2012-01-01', 7)
SET #NextOcurrance = DATEADD(dd, COALESCE((SELECT NumberOfDaysToAdd
FROM OccurrenceExclusions
WHERE ExclusionDate = #NextOcurrance), 0), #NextOcurrance)
Probably the best solution would be to use a calendar table. http://web.archive.org/web/20070611150639/http://sqlserver2000.databases.aspfaq.com/why-should-i-consider-using-an-auxiliary-calendar-table.html
You could then query it like so:
SELECT TOP 1 #NextOccurance = Date
FROM CalendarTable
WHERE Date >= DATEADD(week, #recurranceValue1, #StartDate)
AND DateName = #recurranceValue2
Using the principles the #DanielM set out i changed it to fit my query see below:
BEGIN
SET DATEFIRST 1 --Where 1 = Monday and 7 = Sunday
declare #StartDate datetime -- when the job first recurs
declare #recurrenceValue1 int -- amount of weeks
declare #recurrenceValue2 int -- day of week (mon-sun)
declare #NextOcurrance datetime -- when the job will recur
-- sets #nextoccurence to next date after x amount of weeks
SET #NextOccurance = DATEADD(wk, #recurrenceValue1, #StartDate) -- Add on weeks /* Check if our incrementation falls on the correct day - Adjust if needed */
IF (DATEPART(dw, #NextOccurance) != #recurrenceValue2)
BEGIN
DECLARE #Weekday int = DATEPART(dw, #NextOccurance)
SET #NextOccurance = DATEADD(dd, (#RecurrenceValue2 - #Weekday), #NextOccurance) -- Add to #NextOcurrance the number of days missing to be on the requested day of week
END
END