Определить, выпадает ли занятие на праздник, учитывая даты начала и окончания занятия и даты всех праздников

У меня есть 2 таблицы в базе данных, первая содержит следующую информацию

SECTION_NUMBER SECTION_ID MEETING_ID DAY_TYPE MEETING_NUMBER DATE_TIME_BEGIN DATE_TIME_END 390 166316 102451 1 1 2023-01-23 9:30:00 2023-05-17 10:50:00 390 166316 102451 3 1 2023-01-23 9:30:00 2023-05-17 10:50:00 655 166314 102452 3 1 2023-01-23 12:00:00 2023-05-20 12:00:00 655 166314 102452 7 1 2023-01-23 12:00:00 2023-05-20 12:00:00 283 166315 102453 7 1 2023-01-23 12:00:00 2023-05-20 12:00:00

Следует отметить, что секция будет иметь 1 запись для каждого дня заседания секции, 1 — понедельник, 2 — вторник и т. д. На примере снимка экрана раздел 390 собирается в понедельник и среду, а первый день собрания — 23.01.23, а конец — 17.05.23.

У меня есть второй стол с праздниками

описание DATE_VALUE День недели День Зимние каникулы 2023-01-02 00:00:00 1 М День МЛК 2023-01-16 00:00:00 1 М День Линкольна 2023-02-17 00:00:00 5 Ф Неучебный день 2023-02-18 00:00:00 6 С День Вашингтона 2023-02-20 00:00:00 1 М

У меня есть третья таблица, которая для номера секции показывает дни ее заседаний. Однако он не учитывает праздничные дни.

section_number NbrOfDays 360 33 655 16

Я пытался решить проблему на питоне, но потом узнал, что SSRS поддерживает скрипты на питоне только в версии 2017, а у меня на работе используется 2016.

Какие существуют запросы SQL Server 2016, которые каким-то образом позволили бы мне перебирать строки раздела и проверять, попадает ли праздник между начальной и конечной датой и приходится ли на день, когда раздел встречается, и уменьшать количество дней собрания на 1 для каждый праздник, который соответствует этим критериям?

Используя данные в качестве примера, между датой начала и окончания раздела 390 есть 2 праздничных дня понедельника, поэтому NbrOfDays из третьей таблицы необходимо обновить до 31 с 33, поскольку в ней тип дня собрания 1 = понедельник.

«У меня есть третья таблица, в которой для номера секции указаны дни ее заседаний». - Пожалуйста, предоставьте его определение с некоторыми примерами данных. Без него трудно ответить на ваш вопрос.

J.D. 16.11.2022 06:06

В вашем подходе не используется то, в чем хорош SQL: транзакции на основе наборов. Нет необходимости перебирать записи. Вместо этого напишите запрос, который объединяет данные вместе с учетом всех условий. Оставьте цикл по записям языкам программирования, отличным от SQL. Работая с SQL, всегда думайте о множестве. (Я использую циклы в SQL, но экономно). Опубликуйте данные третьей таблицы, так как ваше описание немного расплывчато.

Tim Jarosz 16.11.2022 07:22
ReactJs | Supabase | Добавление данных в базу данных
ReactJs | Supabase | Добавление данных в базу данных
Это и есть ваш редактор таблиц в supabase.👇
Понимание Python и переход к SQL
Понимание Python и переход к SQL
Перед нами лабораторная работа по BloodOath:
0
2
50
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

Я не думаю, что цифры, которые вы указываете, вполне складываются на основе ваших выборочных данных. например в вашей праздничной таблице есть только одна дата, которая попадает между 2023-01-23 и 2023-05-17, а не две, как вы сказали.

Я не уверен, как вы получаете 16 как общее количество для раздела 655, если вы не считаете выходные?

в любом случае ... Не беспокойтесь о длине этого ответа, фактический бит ответа - это всего несколько строк кода.

В любом случае, я думаю, что вы можете просто создать представление, которое решит это для вас. В третьей таблице нет необходимости, представление заменит третью таблицу.

Извините, если я добавляю комментарии, которые предполагают, что вы не знакомы с базовыми запросами, я предполагаю это только из подхода, который вы хотели использовать.


Настройте данные для репликации вашего образца


Сначала я создал ваши образцы данных со следующими временными таблицами с именами #meets и #hols.

CREATE TABLE #meets(SECTION_NUMBER int, SECTION_ID int, MEETING_ID int, DAY_TYPE int, MEETING_NUMBER int, DATE_TIME_BEGIN DATETIME, DATE_TIME_END datetime)
INSERT INTO #meets VALUES
(390, 166316, 102451, 1, 1, '2023-01-23 9:30:00', '2023-05-17 10:50:00'),
(390, 166316, 102451, 3, 1, '2023-01-23 9:30:00', '2023-05-17 10:50:00'),
(655, 166314, 102452, 3, 1, '2023-01-23 12:00:00', '2023-05-20 12:00:00'),
(655, 166314, 102452, 7, 1, '2023-01-23 12:00:00', '2023-05-20 12:00:00'),
(283, 166315, 102453, 7, 1, '2023-01-23 12:00:00', '2023-05-20 12:00:00')

CREATE TABLE #hols([description] varchar(30), DATE_VALUE date, DayOfWeek int, Day char(1))
INSERT INTO #hols VALUES
('Winter Break'     , '2023-01-02', 1, 'M'),
('MLK Day'          , '2023-01-16', 1, 'M'),
('Lincoln''s Day'   , '2023-02-17', 5, 'F'),
('Non-Teaching Day' , '2023-02-18', 6, 'S'),
('Washington''s Day'    , '2023-02-20', 1, 'M')

Добавить таблицу даты/календаря


Затем я создал таблицу дат. Возможно, у вас уже есть один, так что используйте его, если вы это сделаете, но если нет, создайте его в своей базе данных, поскольку они невероятно полезны для таких вещей.

В этом посте показано, как создать один MakingADateTable

Я включил код здесь на случай, если ссылка не работает.

-- prevent set or regional settings from interfering with 
-- interpretation of dates / literals
SET DATEFIRST  1 -- 1 = Monday, 7 = Sunday

DECLARE @StartDate  date = '20100101'; -- << change this if required

DECLARE @CutoffDate date = DATEADD(DAY, -1, DATEADD(YEAR, 30, @StartDate));

;WITH seq(n) AS 
(
  SELECT 0 UNION ALL SELECT n + 1 FROM seq
  WHERE n < DATEDIFF(DAY, @StartDate, @CutoffDate)
),
d(d) AS 
(
  SELECT DATEADD(DAY, n, @StartDate) FROM seq
),
src AS
(
  SELECT
    TheDate         = CONVERT(date, d),
    TheDay          = DATEPART(DAY,       d),
    TheDayName      = DATENAME(WEEKDAY,   d),
    TheWeek         = DATEPART(WEEK,      d),
    TheISOWeek      = DATEPART(ISO_WEEK,  d),
    TheDayOfWeek    = DATEPART(WEEKDAY,   d),
    TheMonth        = DATEPART(MONTH,     d),
    TheMonthName    = DATENAME(MONTH,     d),
    TheQuarter      = DATEPART(Quarter,   d),
    TheYear         = DATEPART(YEAR,      d),
    TheFirstOfMonth = DATEFROMPARTS(YEAR(d), MONTH(d), 1),
    TheLastOfYear   = DATEFROMPARTS(YEAR(d), 12, 31),
    TheDayOfYear    = DATEPART(DAYOFYEAR, d)
  FROM d
)
SELECT * 
INTO myDateTable -- << CHANGE TABLE NAME HERE IF YOU NEED TO
FROM src
  ORDER BY TheDate
  OPTION (MAXRECURSION 0);

Теперь ответ!


Следующее даст вам каждый номер section_number, день недели для встречи и количество дней.

SELECT 
      SECTION_NUMBER
    , TheDayName
    , NbrOfDays = COUNT(*)
    FROM #meets m 
        JOIN myDateTable d on d.TheDate BETWEEN CAST(m.DATE_TIME_BEGIN as date) AND CAST(m.DATE_TIME_END as date) and m.DAY_TYPE = d.TheDayOfWeek
        LEFT JOIN #hols h on d.TheDate = h.DATE_VALUE
    WHERE h.DATE_VALUE IS NULL
        and d.TheDate >=CAST(GETDATE() as Date) -- optionaly if you want ignore past meetings
    GROUP BY SECTION_NUMBER, DayOfWeek, TheDayName
    ORDER BY SECTION_NUMBER, DayOfWeek

Все, что это делает, — это присоединение каждой даты в таблице myDateTable к таблице #meets, где даты попадают между начальной и конечной датами в таблице #meets, а также объединение в day_type, поэтому возвращаются только совпадающие дни. Затем он оставляет соединения с таблицей #hols, а затем мы включаем только те даты, для которых в таблице #hols не было найдено совпадений. Затем мы просто группируем результаты и подсчитываем, сколько записей в каждой группе.

дает нам это

Если вы просто хотите, чтобы результаты выглядели как в вашем примере, мы можем просто удалить группировку DayOfWeek следующим образом.

SELECT 
      SECTION_NUMBER
    , NbrOfDays = COUNT(*)
    FROM #meets m 
        JOIN myDateTable d on d.TheDate BETWEEN CAST(m.DATE_TIME_BEGIN as date) AND CAST(m.DATE_TIME_END as date) and m.DAY_TYPE = d.TheDayOfWeek
        LEFT JOIN #hols h on d.TheDate = h.DATE_VALUE
    WHERE h.DATE_VALUE IS NULL
        and d.TheDate >=CAST(GETDATE() as Date) -- optionaly if you want ignore past meetings
    GROUP BY SECTION_NUMBER
    ORDER BY SECTION_NUMBER

что дает нам это...

Я оставил там строку для фильтрации прошлых встреч, но вы можете прокомментировать ее, если она вам не нужна.

Если вы хотите превратить эти запросы в постоянные представления, вы можете сделать это с помощью чего-то вроде

CREATE VIEW MeetingCountBySectionAndDay AS 
[copy query from above  here]

Затем вы можете просто запросить представление как таблицу с чем-то вроде

SELECT * FROM MeetingCountBySectionAndDay 

Если праздники добавляются/удаляются или встречи добавляются/редактируются, представление будет автоматически отражать изменения без необходимости выполнять какую-либо работу.

Удивительно и спасибо. Я всегда плохо работал с датами в SQL, но ваша ссылка и пример таблицы дней проясняют ситуацию.

Connor Hale 17.11.2022 09:14

И спасибо, что нашли время преобразовать мои примеры таблиц в SQL, теперь я понимаю, насколько это было бы полезно, если бы я сделал это в своем первом посте.

Connor Hale 17.11.2022 09:33

Без проблем. Как прокомментировали другие, при работе в SQL, если вы обнаружите, что повторяете записи, это, вероятно, (но не всегда) неправильный подход к нему, плюс, как и многие люди в SO, мне просто нравится помогать другим.

Alan Schofield 17.11.2022 10:42

Другие вопросы по теме