{"id":102577,"date":"2024-08-02T14:48:09","date_gmt":"2024-08-02T14:48:09","guid":{"rendered":"https:\/\/www.red-gate.com\/simple-talk\/?p=102577"},"modified":"2026-05-08T10:14:43","modified_gmt":"2026-05-08T10:14:43","slug":"days-of-the-week-bitwise-edition","status":"publish","type":"post","link":"https:\/\/www.red-gate.com\/simple-talk\/databases\/sql-server\/days-of-the-week-bitwise-edition\/","title":{"rendered":"Storing Weekday Sets in SQL Server Using Bitwise Flags: Schedules and Recurring Jobs"},"content":{"rendered":"\n<p><b>A compact way to store &#8216;which days of the week does this happen?&#8217; in SQL Server is as a bitwise flag set: Sunday = 1, Monday = 2, Tuesday = 4, Wednesday = 8, Thursday = 16, Friday = 32, Saturday = 64. A Monday\/Wednesday\/Friday schedule is 2 + 8 + 32 = 42, stored in a single TINYINT column. Testing whether a given day is in the set is a bitwise AND: (day_flag &amp; schedule) != 0. <\/b><\/p>\n\n\n\n<p><b>This article walks through the storage scheme, a function that tests weekday membership for a given date, a second function that expands a bit-pattern schedule into the list of concrete dates within a range, and discussion of future refinements. The technique applies anywhere a small fixed set of options needs compact storage: recurring job schedules, business-day calendars, user-preference toggles, feature flags.<\/b><\/p>\n\n\n<p>I recently had to help support synchronization and distribution of workloads between multiple servers. Some of this work involves Task Scheduler and, be honest, who knew Task Scheduler was <a href=\"https:\/\/learn.microsoft.com\/en-us\/windows\/win32\/taskschd\/task-scheduler-reference\" target=\"_blank\" rel=\"noopener\">this complicated<\/a>? On different servers, we wanted jobs to trigger on different days of the week. The way that Task Scheduler handles this programmatically is through a numeric property called <code style=\"font-family: consolas; background: #e4e4e4; padding: 3px 4px 1px 4px; border-radius: 3px;\">WeeklyTrigger.DaysOfWeek<\/code>, where the following coefficients are packed together into a single value:<\/p>\n<pre class=\"theme:tomorrow-night font:consolas font-size:14 line-height:16 decode-attributes:false tab-convert:true lang:rexx decode:true whitespace-before:1 whitespace-after:1\"> 1  = Sunday\n 2  = Monday\n 4  = Tuesday\n 8  = Wednesday\n 16 = Thursday\n 32 = Friday\n 64 = Saturday<\/pre>\n<p>For example, <code style=\"font-family: consolas; background: #e4e4e4; padding: 3px 4px 1px 4px; border-radius: 3px;\">62<\/code> would be derived from <code style=\"font-family: consolas; background: #e4e4e4; padding: 3px 4px 1px 4px; border-radius: 3px;\">2 + 4 + 8 + 16 + 32<\/code>, which means the schedule is for every weekday (excluding Saturday and Sunday), while <code style=\"font-family: consolas; background: #e4e4e4; padding: 3px 4px 1px 4px; border-radius: 3px;\">65<\/code> would come from <code style=\"font-family: consolas; background: #e4e4e4; padding: 3px 4px 1px 4px; border-radius: 3px;\">64 + 1<\/code>, meaning <em>only<\/em> Saturday and Sunday. You can visualize this as follows, with a couple more examples:<\/p>\n<p><img decoding=\"async\" style=\"width: 80%; min-width: 320px;\" src=\"https:\/\/www.red-gate.com\/simple-talk\/wp-content\/uploads\/2024\/06\/bitwise-vis-examples.png\" alt=\"Examples of bitwise DaysOfWeek\" \/><\/p>\n<p>You can read more about this specific enum as it relates to Task Scheduler <a href=\"https:\/\/learn.microsoft.com\/en-us\/windows\/win32\/taskschd\/weeklytrigger-daysofweek\" target=\"_blank\" rel=\"noopener\">here<\/a> and <a href=\"https:\/\/learn.microsoft.com\/en-us\/windows\/win32\/taskschd\/taskschedulerschema-daysofweek-weeklyscheduletype-element\" target=\"_blank\" rel=\"noopener\">here<\/a>.<\/p>\n<p>I&#8217;m not a big fan of &#8220;hiding&#8221; values in a single integer and requiring bitwise operations to extract them. It seems an optimization holdover from the days of punch-cards and floppy disks, where space was <em>way<\/em> more valuable than logic and self-documentation. I&#8217;ve written about it before in <a href=\"https:\/\/sqlperformance.com\/2012\/08\/t-sql-queries\/dry-principle-bitwise-operations\" target=\"_blank\" rel=\"noopener\">When the DRY principle doesn&#8217;t apply : BITWISE operations in SQL Server<\/a>. Still, in 2024, we can&#8217;t escape it. I don&#8217;t have to deal with bitwise in SQL Server very often, but I recently came across&#8230;<\/p>\n<h4>This use case<\/h4>\n<p>To interface between Task Scheduler and SQL Server, I wanted a way to translate the enum to produce different output depending on the consumer. When I know the trigger value from Task Scheduler but need to expose it to something in (or from) SQL Server, I wanted a table-valued function to return the day(s) of the week given any specific enum value. I might want one of the following output styles, say, from an enum value of 3 (which means Sunday and Monday):<\/p>\n<p><img loading=\"lazy\" decoding=\"async\" class=\"alignnone  wp-image-103377\" src=\"https:\/\/www.red-gate.com\/simple-talk\/wp-content\/uploads\/2024\/07\/Screenshot-2024-07-26-145804.png\" alt=\"\" width=\"574\" height=\"131\" \/><\/p>\n<p>\n\n\n\n<div style=\"background: #fff; width: 100%; margin-top: -32px;\">\u00a0<\/div>\n<p>The first thing I had to do was define the enum in T-SQL somehow. One way is a <a href=\"https:\/\/learn.microsoft.com\/en-us\/sql\/t-sql\/queries\/table-value-constructor-transact-sql?view=sql-server-ver16\" target=\"_blank\" rel=\"noopener\">table value constructor<\/a>:<\/p>\n<pre class=\"theme:tomorrow-night font:consolas font-size:14 line-height:16 decode-attributes:false tab-convert:true lang:tsql decode:true whitespace-before:1 whitespace-after:1\"> SELECT enum, dayname, dayofweek FROM \n (\n   VALUES (1,  'Sunday',    1),\n          (2,  'Monday',    2),\n          (4,  'Tuesday',   3),\n          (8,  'Wednesday', 4),\n          (16, 'Thursday',  5),\n          (32, 'Friday',    6),\n          (64, 'Saturday',  7)\n  ) AS denum (enum, dayname, dayofweek);\n<\/pre>\n<p>The output of this in isolation should not be shocking:<\/p>\n<p><img decoding=\"async\" style=\"width: 20%; min-width: 160px;\" src=\"https:\/\/www.red-gate.com\/simple-talk\/wp-content\/uploads\/2024\/08\/enum-output-a.png\" \/><\/p>\n<p>If I know the <code style=\"font-family: consolas; background: #e4e4e4; padding: 3px 4px 1px 4px; border-radius: 3px;\">DaysOfWeek<\/code> value, I can pull the relevant values from this constructor by simply <code style=\"font-family: consolas; background: #e4e4e4; padding: 3px 4px 1px 4px; border-radius: 3px;\">AND<\/code>ing the coefficient with the value passed in:<\/p>\n<pre class=\"theme:tomorrow-night font:consolas font-size:14 line-height:16 decode-attributes:false tab-convert:true lang:tsql decode:true whitespace-before:1 whitespace-after:1\"> DECLARE @Enum tinyint = 42;\n\n SELECT dayname FROM \n (\n   VALUES (1,  'Sunday',    1),\n          (2,  'Monday',    2),\n          (4,  'Tuesday',   3),\n          (8,  'Wednesday', 4),\n          (16, 'Thursday',  5),\n          (32, 'Friday',    6),\n          (64, 'Saturday',  7)\n  ) AS denum (enum, dayname, dayofweek)\n  WHERE enum &amp; @Enum &gt; 0;<\/pre>\n<p>Output:<\/p>\n<p><img decoding=\"async\" style=\"width: 15%; min-width: 120px;\" src=\"https:\/\/www.red-gate.com\/simple-talk\/wp-content\/uploads\/2024\/06\/bitwise-second-output.png\" \/><\/p>\n<p>Now I know how I can put this in a function:<\/p>\n<pre class=\"theme:tomorrow-night font:consolas font-size:14 line-height:16 decode-attributes:false tab-convert:true lang:tsql decode:true whitespace-before:1 whitespace-after:1\"> CREATE OR ALTER FUNCTION dbo.GetDaysOfWeekFromEnum\n (\n   @Enum tinyint\n )\n RETURNS TABLE\n AS\n   RETURN (\n     WITH cte AS\n     (\n       SELECT enum, dayname, dayofweek FROM \n       (\n         VALUES (1,  'Sunday',    1),\n                (2,  'Monday',    2),\n                (4,  'Tuesday',   3),\n                (8,  'Wednesday', 4),\n                (16, 'Thursday',  5),\n                (32, 'Friday',    6),\n                (64, 'Saturday',  7)\n       ) AS denum (enum, dayname, dayofweek)\n       WHERE enum &amp; @Enum &gt; 0\n     )\n     SELECT dayname FROM cte\n   );<\/pre>\n<p>If I call it with various arguments:<\/p>\n<pre class=\"theme:tomorrow-night font:consolas font-size:14 line-height:16 decode-attributes:false tab-convert:true lang:tsql decode:true whitespace-before:1 whitespace-after:1\"> SELECT dayname FROM dbo.GetDaysOfWeekFromEnum(62);\n SELECT dayname FROM dbo.GetDaysOfWeekFromEnum(65);\n SELECT dayname FROM dbo.GetDaysOfWeekFromEnum(34);\n SELECT dayname FROM dbo.GetDaysOfWeekFromEnum(42);<\/pre>\n<p>I get the same output illustrated above:<\/p>\n<p><img decoding=\"async\" style=\"width: 80%; min-width: 320px;\" src=\"https:\/\/www.red-gate.com\/simple-talk\/wp-content\/uploads\/2024\/06\/bitwise-third-output.png\" \/><\/p>\n<p>This handles the &#8220;set&#8221; style of output, where each day is on its own row. In order to derive the different styles, I can add an argument and then create a union between the different possibilities:<\/p>\n<pre class=\"theme:tomorrow-night font:consolas font-size:14 line-height:16 decode-attributes:false tab-convert:true lang:tsql decode:true whitespace-before:1 whitespace-after:1\"> CREATE OR ALTER FUNCTION dbo.GetDaysOfWeekFromEnum\n (\n   @Enum  tinyint,\n   @Style varchar(4) \/* set | csv | json *\/\n )\n RETURNS TABLE\n AS\n   RETURN (\n     WITH cte AS\n     (\n       SELECT enum, dayname, dayofweek FROM \n       (\n         VALUES (1,  'Sunday',    1),\n                (2,  'Monday',    2),\n                (4,  'Tuesday',   3),\n                (8,  'Wednesday', 4),\n                (16, 'Thursday',  5),\n                (32, 'Friday',    6),\n                (64, 'Saturday',  7)\n       ) AS denum (enum, dayname, dayofweek)\n       WHERE enum &amp; @Enum &gt; 0\n     )\n     SELECT dayname FROM\n     (\n       SELECT dayname \n         FROM cte \n        WHERE @Style = 'set'\n\n       UNION ALL\n\n       SELECT STRING_AGG(dayname, ',')\n       WITHIN GROUP (ORDER BY dayofweek)\n         FROM cte \n        WHERE @Style = 'csv'\n   \n       UNION ALL \n   \n       SELECT Day FROM \n       (\n         SELECT dayname FROM\n         (\n           SELECT dayname \n             FROM cte\n            WHERE @Style = 'json'\n         ) AS i\n         ORDER BY dayofweek\n         FOR JSON PATH, WITHOUT_ARRAY_WRAPPER\n       ) AS o(Day)\n     ) AS f WHERE dayname IS NOT NULL\n   );<\/pre>\n<p>If I ask for the days for <code style=\"font-family: consolas; background: #e4e4e4; padding: 3px 4px 1px 4px; border-radius: 3px;\">62<\/code> in each style:<\/p>\n<pre class=\"theme:tomorrow-night font:consolas font-size:14 line-height:16 decode-attributes:false tab-convert:true lang:tsql decode:true whitespace-before:1 whitespace-after:1\"> SELECT dayname FROM dbo.GetDaysOfWeekFromEnum(62, 'set');\n SELECT dayname FROM dbo.GetDaysOfWeekFromEnum(62, 'csv');\n SELECT dayname FROM dbo.GetDaysOfWeekFromEnum(62, 'json');<\/pre>\n<p>You can see the different output:<\/p>\n<p><img decoding=\"async\" style=\"width: 80%; min-width: 320px;\" src=\"https:\/\/www.red-gate.com\/simple-talk\/wp-content\/uploads\/2024\/06\/bitwise-fourth-output.png\" \/><\/p>\n<p>Now all of my PowerShell and C# can just pass the trigger value into SQL Server and not have to think about it. This works great for me, because T-SQL is where I am much more comfortable.<\/p>\n<p>Note that there&#8217;s not a tidy way in this function to ensure that the <code style=\"font-family: consolas; background: #e4e4e4; padding: 3px 4px 1px 4px; border-radius: 3px;\">set<\/code> version will return the days in proper order, unless you output the <code style=\"font-family: consolas; background: #e4e4e4; padding: 3px 4px 1px 4px; border-radius: 3px;\">dayofweek<\/code> column (and a dummy column for the other two union queries).<\/p>\n<h3>A second use case<\/h3>\n<p>Sometimes I need to know all the dates a job will run within a date range (say, a month). For this I wanted a different TVF that returns a list of dates, on the days of the week represented by the enum, between <code style=\"font-family: consolas; background: #e4e4e4; padding: 3px 4px 1px 4px; border-radius: 3px;\">@StartDate<\/code> and <code style=\"font-family: consolas; background: #e4e4e4; padding: 3px 4px 1px 4px; border-radius: 3px;\">@EndDate<\/code>. What this really means is, <a href=\"https:\/\/www.red-gate.com\/simple-talk\/databases\/sql-server\/t-sql-programming-sql-server\/generate-series-to-build-a-set\/\" target=\"_blank\" rel=\"noopener\">generate a series of dates<\/a> from the start date to the end date, and from that series, return the dates that match any of the weekdays present in the <code style=\"font-family: consolas; background: #e4e4e4; padding: 3px 4px 1px 4px; border-radius: 3px;\">DaysOfWeek<\/code> value.<\/p>\n<p>In SQL Server 2022, we can use <code style=\"font-family: consolas; background: #e4e4e4; padding: 3px 4px 1px 4px; border-radius: 3px;\">GENERATE_SERIES<\/code><sup>*<\/sup> to create a set of days. For example, to get all the dates in December 2024:<\/p>\n<pre class=\"theme:tomorrow-night font:consolas font-size:14 line-height:16 decode-attributes:false tab-convert:true lang:tsql decode:true whitespace-before:1 whitespace-after:1\"> DECLARE @StartDate date = '20241201',\n         @EndDate   date = '20241231';\n\n SELECT DATEADD(DAY, value, @StartDate)\n   FROM GENERATE_SERIES(0, DATEDIFF(DAY, @StartDate, @EndDate));<\/pre>\n<p><em>I&#8217;m not going to prove it with a screenshot, but that returns a set of 31 days, from December 1st through the 31st.<\/em><\/p>\n<p>Next, to match these dates up with the days of the week present in the enum, I can extract the <code style=\"font-family: consolas; background: #e4e4e4; padding: 3px 4px 1px 4px; border-radius: 3px;\">DATENAME<\/code> for the day of week from each date, and join on that. For example:<\/p>\n<pre class=\"theme:tomorrow-night font:consolas font-size:14 line-height:16 decode-attributes:false tab-convert:true lang:tsql decode:true whitespace-before:1 whitespace-after:1\"> CREATE OR ALTER FUNCTION dbo.GetDatesFromEnumInRange\n ( \n   @Enum       tinyint,\n   @StartDate  date,\n   @EndDate    date\n )\n RETURNS TABLE\n AS \n   RETURN\n   (\n     WITH daysofweek AS\n     (\n       SELECT dayname FROM \n       (\n         VALUES (1,  'Sunday',    1),\n                (2,  'Monday',    2),\n                (4,  'Tuesday',   3),\n                (8,  'Wednesday', 4),\n                (16, 'Thursday',  5),\n                (32, 'Friday',    6),\n                (64, 'Saturday',  7)\n       ) AS denum (enum, dayname, dayofweek)\n       WHERE enum &amp; @Enum &gt; 0\n     ),\n     days(Date) AS\n     (\n       SELECT DATEADD(DAY, value, @StartDate)\n         FROM GENERATE_SERIES(0, DATEDIFF(DAY, @StartDate, @EndDate))\n     )\n     SELECT days.Date,\n            daysofweek.dayname\n       FROM days\n      INNER JOIN daysofweek\n         ON DATENAME(WEEKDAY, days.Date) = daysofweek.dayname\n );<\/pre>\n<div style=\"margin: 10px 20px 25px 20px; background: #dedede; border-radius: 20px; padding: 12px 20px; font-size: 0.875rem; line-height: 1.4rem;\">\n<p><span style=\"color: #000000;\"><b><sup>*<\/sup> Note<\/b>: I have to rely on either @@LANGUAGE being some form of English, or use the dayofweek value and rely on @@DATEFIRST. If you use a different language, or a different\u00a0 DATEFIRST setting, you could just construct your enum accordingly. Just remember that any user could have a different setting in their own session.<\/span><\/p>\n<p><span style=\"color: #000000;\">Also, GENERATE_SERIES requires SQL Server 2022 or Azure SQL Database; see alternatives for earlier versions in <a style=\"color: #000000;\" href=\"https:\/\/www.red-gate.com\/simple-talk\/databases\/sql-server\/t-sql-programming-sql-server\/generate-series-to-build-a-set\/\" target=\"_blank\" rel=\"noopener\">this post<\/a>.<\/span><\/p>\n<\/div>\n<p>If I call that for the enum <code style=\"font-family: consolas; background: #e4e4e4; padding: 3px 4px 1px 4px; border-radius: 3px;\">62<\/code>, for the month of December:<\/p>\n<pre class=\"theme:tomorrow-night font:consolas font-size:14 line-height:16 decode-attributes:false tab-convert:true lang:tsql decode:true whitespace-before:1 whitespace-after:1\"> DECLARE @Enum      tinyint = 62,\n         @StartDate date    = '20241201',\n         @EndDate   date    = '20241231';\n\n SELECT Date, dayname\n   FROM dbo.GetDatesFromEnumInRange\n        (@Enum, @StartDate, @EndDate)\n  ORDER BY Date;<\/pre>\n<p>Here is the output:<\/p>\n<p><img decoding=\"async\" style=\"width: 20%; min-width: 160px;\" src=\"https:\/\/www.red-gate.com\/simple-talk\/wp-content\/uploads\/2024\/06\/bitwise-fifth-output.png\" \/><\/p>\n<h3>Future enhancements?<\/h3>\n<p>There are definitely some fixes for the future. One is that, now that I have two separate functions referencing it, I should be storing my enum in a table somewhere. The other is that I&#8217;d like to fix it so that I don&#8217;t have to rely on a stable <code style=\"font-family: consolas; background: #e4e4e4; padding: 3px 4px 1px 4px; border-radius: 3px;\">DATEFIRST<\/code> setting but, thankfully, the function rarely gets invoked manually.<\/p>\n<p>Depending on your use case, you may also want to integrate a <a href=\"https:\/\/www.mssqltips.com\/sqlservertip\/4054\/creating-a-date-dimension-or-calendar-table-in-sql-server\/\" target=\"_blank\" rel=\"noopener\">calendar table<\/a>, so that you can highlight (or even exclude) holidays or scheduled maintenance windows from certain results.<\/p>\n\n","protected":false},"excerpt":{"rendered":"<p>Store and query sets of weekdays in SQL Server using bitwise flags &#8211; each day encoded as a power of two (Sunday=1, Monday=2, &#8230;, Saturday=64). Includes T-SQL for testing membership, expanding bit patterns to date ranges, and applying the technique to job schedules, recurring appointments, and business-day calendars.&hellip;<\/p>\n","protected":false},"author":341115,"featured_media":103371,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"_acf_changed":false,"footnotes":""},"categories":[53,143524,143531],"tags":[4151],"coauthors":[158980],"class_list":["post-102577","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-featured","category-sql-server","category-t-sql-programming-sql-server","tag-sql-server"],"acf":[],"_links":{"self":[{"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/posts\/102577","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/users\/341115"}],"replies":[{"embeddable":true,"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/comments?post=102577"}],"version-history":[{"count":45,"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/posts\/102577\/revisions"}],"predecessor-version":[{"id":110396,"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/posts\/102577\/revisions\/110396"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/media\/103371"}],"wp:attachment":[{"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/media?parent=102577"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/categories?post=102577"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/tags?post=102577"},{"taxonomy":"author","embeddable":true,"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/coauthors?post=102577"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}