Общее число дней в разделенном диапазоне дат, не равное числу дней в разделенном диапазоне дат, почему? - PullRequest
2 голосов
/ 30 апреля 2019

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

enter image description here

SELECT
[Year1_2_3] = DATEDIFF(DAY, '2016-08-20', '2019-08-19')

SELECT
   [Year1_2_3_Sum] = [Q].[Year1] + [Q].[Year2] + [Q].[Year3]
 , [Year1]         = [Q].[Year1]
 , [Year2]         = [Q].[Year2]
 , [Year3]         = [Q].[Year3]
FROM
   (
   SELECT
   [Year1] = DATEDIFF(DAY, '2016-08-20', '2017-08-19')
 , [Year2] = DATEDIFF(DAY, '2017-08-20', '2018-08-19')
 , [Year3] = DATEDIFF(DAY, '2018-08-20', '2019-08-19')
   ) [Q]

    SELECT
   [Order_Year1_2_3]            = [Q].[Order_Year1_2_3]
 , [Order_Amount]               = [Q].[Order_Amount]
 , [Order_AmountPerDay]         = [Q].[Order_AmountPerDay]
 , [OrderLine_Year1_Days]       = [Q].[OrderLine_Year1_Days]
 , [OrderLine_Year2_Days]       = [Q].[OrderLine_Year2_Days]
 , [OrderLine_Year3_Days]       = [Q].[OrderLine_Year3_Days]
 , [OrderLine_Year1_Amount]     = [Q].[Order_AmountPerDay] * [Q].[OrderLine_Year1_Days]
 , [OrderLine_Year2_Amount]     = [Q].[Order_AmountPerDay] * [Q].[OrderLine_Year2_Days]
 , [OrderLine_Year3_Amount]     = [Q].[Order_AmountPerDay] * [Q].[OrderLine_Year3_Days]
 , [OrderLine_Year1_2_3_Amount] = ([Q].[Order_AmountPerDay] * [Q].[OrderLine_Year1_Days]) + ([Q].[Order_AmountPerDay] * [Q].[OrderLine_Year2_Days]) + ([Q].[Order_AmountPerDay] * [Q].[OrderLine_Year3_Days])
FROM
   (
   SELECT
   [Order_Year1_2_3]      = DATEDIFF(DAY, '2016-08-20', '2019-07-19')
 , [Order_Amount]         = 145000.00
 , [Order_AmountPerDay]   = CONVERT(FLOAT, 145000) / CONVERT(FLOAT, DATEDIFF(DAY, '2016-08-20', '2019-07-19'))
 , [OrderLine_Year1_Days] = CONVERT(FLOAT, DATEDIFF(DAY, '2016-08-20', '2017-08-19'))
 , [OrderLine_Year2_Days] = CONVERT(FLOAT, DATEDIFF(DAY, '2017-08-20', '2018-08-19'))
 , [OrderLine_Year3_Days] = CONVERT(FLOAT, DATEDIFF(DAY, '2018-08-20', '2019-07-19'))
   ) [Q]

Я добавил второй запрос здесь с лучшим примером - и показателем количества для иллюстрации.Мне нужно сохранить правильные даты от / до для каждого года (на самом деле это будут три отдельные строки с соответствующими датами от / до), но я все равно смогу свернуть на уровень «заголовка» и получить правильную настройку.

1 Ответ

3 голосов
/ 30 апреля 2019

DATEDIFF(day, ...) возвращает количество полуночников между указанными датами.

Например, DATEDIFF(DAY, '2016-08-20', '2016-08-21') возвращает 1.

Если вы думаете о [2016-08-20 -- 2016-08-21] как о замкнутом интервале, который включает обе даты, то это два дня.

Обычно с интервалами легче работать, когда вы рассматриваете их как интервалы замкнутого-открытого типа. При таком подходе у вас будет [2016-08-20 -- 2016-08-22) для определения того же двухдневного интервала.

Я бы рекомендовал принять этот подход и всегда рассматривать начальную дату / время интервала как включающее, а конечную дату / время интервала как исключительное. Это соглашение, естественно, будет работать правильно для интервалов, которые также имеют временную составляющую. Например, [2016-08-20T00:00:00 -- 2016-08-22T00:00:00) ясно говорит, что интервал составляет два полных дня, 48 часов.

Ваш пример будет выглядеть так:

SELECT
[Year1_2_3] = DATEDIFF(DAY, '2016-08-20', '2019-08-20')

SELECT
   [Year1_2_3_Sum] = [Q].[Year1] + [Q].[Year2] + [Q].[Year3]
 , [Year1]         = [Q].[Year1]
 , [Year2]         = [Q].[Year2]
 , [Year3]         = [Q].[Year3]
FROM
   (
   SELECT
   [Year1] = DATEDIFF(DAY, '2016-08-20', '2017-08-20')
 , [Year2] = DATEDIFF(DAY, '2017-08-20', '2018-08-20')
 , [Year3] = DATEDIFF(DAY, '2018-08-20', '2019-08-20')
   ) [Q]

Результат

+-----------+
| Year1_2_3 |
+-----------+
|      1095 |
+-----------+

+---------------+-------+-------+-------+
| Year1_2_3_Sum | Year1 | Year2 | Year3 |
+---------------+-------+-------+-------+
|          1095 |   365 |   365 |   365 |
+---------------+-------+-------+-------+

Кроме того, взгляните на статью Вредные привычки: неправильная обработка запросов даты / диапазона Аарона Бертрана. Он показывает там несколько примеров того, как усыновление [закрыто; открытые) интервалы для диапазонов даты / времени часто лучше.


Если ваши конечные пользователи интерпретируют диапазон дат как [включительно; включительно], затем настройте конечную дату на 1 день во время презентации пользователю , в самый последний момент, когда вы хотите показать результат пользователю, но внутри базы данных и кода всегда работают с [закрыто; открытые) интервалы.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...