#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))