Проверьте, присутствует ли данный месяц дата в данных, содержащих диапазон месяц дата

#sql #sql-server #tsql #datetime #sql-server-2000

#sql #sql-сервер #tsql #дата-время #sql-server-2000

Вопрос:

Вот мой запрос:

 DECLARE @MM INT -- Current month
DECLARE @DD INT -- Current date

SET @MM = 1 -- For testing, set it to January
SET @DD = 1 -- For testing, set it to 01

SELECT xxxID, xxxFK, StartMonth, StartDate, StopMonth, StopDate, NULL AS OKorNOT
FROM xxxTable
ORDER BY xxxFK
  

И вот данные:

 xxxID            xxxFK       StartMonth  StartDate   StopMonth   StopDate    OKorNOT     
---------------- ----------- ----------- ----------- ----------- ----------- ----------- 
8                2287        11          15          1           2           NULL
4                2290        2           1           2           21          NULL
2                2306        9           15          10          31          NULL
3                2306        1           3           1           20          NULL
9                2661        11          15          1           3           NULL
10               2661        5           5           5           31          NULL
5                3778        6           2           9           5           NULL
6                3778        1           1           3           31          NULL
7                3778        5           10          5           31          NULL
1                3778        12          10          12          31          NULL
  

Мне нужно заполнить столбец OKorNot значением 1/0 в зависимости от того, находится ли данная дата месяца между StartMonth-StartDate и StopMonth-StopDate. Кстати, это SQL Server 2000.

Редактировать

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

Ответ №1:

Используя только целочисленные операции и воображаемый календарь на 384 дня

Поскольку ваши даты представляют собой комбинации месяца и дня, я попытался создать целое число для каждой такой комбинации, целое число, которое является уникальным и также сохраняет порядок. Чтобы вычисления были как можно более простыми, мы изобретаем новый календарь, в котором все месяцы имеют ровно 32 дня, и мы действуем так, как будто наши даты взяты из этого календаря. Затем, чтобы узнать, сколько дней прошло с 1 января, у нас есть формула:

DaysPast = 32 * month day

(Хорошо, так и должно быть 32 * (month-1) (day-1) но так проще, и мы хотим сравнивать даты только относительно друг друга, а не с 1 января. И результат по-прежнему уникален для каждой даты).

Поэтому сначала мы вычисляем DaysPast для нашей контрольной даты:

SET @CHECK = 32 * @MM @DD

Затем мы вычисляем DaysPast для всех дат (как начальных, так и конечных) в нашей таблице:

   ( SELECT *
         , (32 * StartMonth   StartDate) AS Start
         , (32 * StopMonth    StopDate ) AS Stop
    FROM xxxTable
  ) AS temp
  

Тогда у нас есть два случая.

  • Первый случай, когда Start = (8-Feb) и Stop = (23-Nov) .

Тогда первое условие @CHECK BETWEEN Start AND Stop будет истинным, и даты между началом и остановкой будут в порядке.

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

  • Второй случай, когда Start = (23-Nov) и Stop = (8-Feb) . :

Тогда первое условие @CHECK BETWEEN Start AND Stop будет ложным, потому что Start больше Stop, поэтому никакие даты не могут соответствовать этому условию.

Второе условие Stop < Start будет истинным, поэтому мы также проверяем, присутствует ли
@CHECK не является BETWEEN (9-Feb) AND (22-Nov)
для сопоставления дат, которые находятся до (9-Feb) или после (22-Nov) .

 DECLARE @CHECK INT
SET @CHECK = 32 * @MM   @DD

SELECT *
     , CASE WHEN
           @CHECK BETWEEN Start AND Stop 
           OR ( Stop < Start 
               AND @CHECK NOT BETWEEN Stop 1 AND Start-1
              )
         THEN 1
         ELSE 0
       END
       AS OKorNOT
FROM 
  ( SELECT *
         , (32 * StartMonth   StartDate) AS Start
         , (32 * StopMonth    StopDate ) AS Stop
    FROM xxxTable
  ) AS temp
ORDER BY xxxFK
  

Комментарии:

1. Это выглядит проще для понимания, но я был бы признателен, если бы вы могли в одной / двух строках объяснить, как обрабатывается перенос по регистру.

2. Отлично, меня смутил второй случай, который вы только что объяснили. Я вернулся к работе, поэтому теперь могу протестировать запрос. Я проверю наличие пограничных случаев, но это выглядит многообещающе.

Ответ №2:

Было бы проще, если бы вы сохраняли даты как, ну, даты…

В любом случае, что-то вроде этого. Я не тестировал. И вам нужно разобраться с границей года, что я и сделал

 SELECT
    xxxID, xxxFK, StartMonth, StartDate, StopMonth, StopDate,
    CASE 
       WHEN
          FullStart <= FullStop AND 
            DATEADD(month, @MM-1, DATEADD(day, @DD-1, 0)) BETWEEN FullStart AND FullStop
                      THEN 1
       WHEN
          FullStart > FullStop AND 
            DATEADD(month, @MM-1, DATEADD(day, @DD-1, 0)) BETWEEN
                    FullStart AND DATEADD(year, 1, FullStop)
                      THEN 1  
       ELSE 0
    END AS OKOrNot
FROM
    (
    SELECT
        xxxID, xxxFK, StartMonth, StartDate, StopMonth, StopDate,
        DATEADD(month, StartMonth-1, DATEADD(day, StartDate-1, 0)) AS FullStart,
        DATEADD(month, StopMonth-1, DATEADD(day, StopDate-1, 0)) AS FullStop
    FROM xxxTable
    ) foo
ORDER BY xxxFK
  

редактировать: добавлено «-1» ко всем значениям: если мы уже в январе, не добавляйте еще один месяц…

Комментарии:

1. Я проверил, это не удалось для случая № 1 (8, 2287, 11, 15, 1, 2, 0) с датой Jan/1 … должно было вернуться 1 / true / OK.

2. @Salman A: вы пробовали играть с этим? У меня нет SQL Server передо мной

3. Измените FullStop определение столбца следующим образом: DATEADD(year, CASE StopMonth * 100 StopDay < StartMonth * 100 StartDay THEN 1 ELSE 0 END, DATEADD(month, StopMonth-1, DATEADD(day, StopDate-1, 0))) AS FullStop , т.е. текущее выражение должно быть вложено в добавленное DATEADD(year, ...) .

4. @gbn: Когда FullStart > FullStop требуется еще одно условие. Как бы то ни было, только даты в конце года, после FullStart , будут в порядке. Даты в начале года, предшествующие FullStop , также должны быть проверены.

5. @gbn: Похоже, что у вас есть, извините.

Ответ №3:

 SELECT *
FROM xxxTable
WHERE (StartMonth < StopMonth OR StartMonth = StopMonth AND StartDate<=StopDate)
    AND (@MM > StartMonth OR @MM = StartMonth AND @DD >= StartDate)
    AND (@MM < StopMonth OR @MM = StopMonth AND @DD <= StopDate)
    OR (StartMonth > StopMonth OR StartMonth = StopMonth AND StartDate>StopDate)
        AND ((@MM > StartMonth OR @MM = StartMonth AND @DD >= StartDate)
            OR (@MM < StopMonth OR @MM = StopMonth AND @DD <= StopDate))