Этот ответ соответствует формату T-SQL. Я рассматриваю эту проблему как одно из линейно-временного расстояния между двумя точками даты в формате datetime , называем их Time1 и Time2; Time1 должен быть выровнен со «старым по времени» значением, с которым вы имеете дело (скажем, Дата рождения или дата создания виджета или Дата начала поездки), а Time2 должно быть выровнено со значением «новее по времени» (например, дата снимка) или дата завершения виджета или достигнутая контрольная точка поездки).
DECLARE @Time1 DATETIME
SET @Time1 = '12/14/2015'
DECLARE @Time2 DATETIME
SET @Time2 = '12/15/2016'
Решение использует простые измерения, преобразования и вычисления последовательных пересечений нескольких циклов различной длины; здесь: Century, Decade, Year, Month, Day (спасибо календарю майя за концепцию!). Небольшое спасибо: я благодарю других участников Stack Overflow за то, что они продемонстрировали мне некоторые функции компонентов в этом процессе, которые я собрал вместе. Я положительно оценил их в свое время на этом форуме.
Сначала создайте горизонт, который представляет собой линейный набор пересечений циклов Века, Десятилетия, Год, Месяц, с приращением по месяцам. Для этого используйте декартову функцию перекрестного соединения. (Думайте об этом как о создании ткани, из которой мы будем отрезать отрезок между двумя точками «гггг-мм» для измерения расстояния):
SELECT
Linear_YearMonths = (centuries.century + decades.decade + years.[year] + months.[Month]),
1 AS value
INTO #linear_months
FROM
(SELECT '18' [century] UNION ALL
SELECT '19' UNION ALL
SELECT '20') centuries
CROSS JOIN
(SELECT '0' [decade] UNION ALL
SELECT '1' UNION ALL
SELECT '2' UNION ALL
SELECT '3' UNION ALL
SELECT '4' UNION ALL
SELECT '5' UNION ALL
SELECT '6' UNION ALL
SELECT '7' UNION ALL
SELECT '8' UNION ALL
SELECT '9') decades
CROSS JOIN
(SELECT '1' [year] UNION ALL
SELECT '2' UNION ALL
SELECT '3' UNION ALL
SELECT '4' UNION ALL
SELECT '5' UNION ALL
SELECT '6' UNION ALL
SELECT '7' UNION ALL
SELECT '8' UNION ALL
SELECT '9' UNION ALL
SELECT '0') years
CROSS JOIN
(SELECT '-01' [month] UNION ALL
SELECT '-02' UNION ALL
SELECT '-03' UNION ALL
SELECT '-04' UNION ALL
SELECT '-05' UNION ALL
SELECT '-06' UNION ALL
SELECT '-07' UNION ALL
SELECT '-08' UNION ALL
SELECT '-09' UNION ALL
SELECT '-10' UNION ALL
SELECT '-11' UNION ALL
SELECT '-12') [months]
ORDER BY 1
Затем преобразуйте точки даты Time1 и Time2 в формат «гггг-мм» (думайте о них как о точечных координатах на всей ткани). Сохраните также исходные datetime версии точек:
SELECT
Time1 = @Time1,
[YYYY-MM of Time1] = CASE
WHEN LEFT(MONTH(@Time1),1) <> '1' OR MONTH(@Time1) = '1'
THEN (CAST(YEAR(@Time1) AS VARCHAR) + '-' + '0' + CAST(MONTH(@Time1) AS VARCHAR))
ELSE (CAST(YEAR(@Time1) AS VARCHAR) + '-' + CAST(MONTH(@Time1) AS VARCHAR))
END,
Time2 = @Time2,
[YYYY-MM of Time2] = CASE
WHEN LEFT(MONTH(@Time2),1) <> '1' OR MONTH(@Time2) = '1'
THEN (CAST(YEAR(@Time2) AS VARCHAR) + '-' + '0' + CAST(MONTH(@Time2) AS VARCHAR))
ELSE (CAST(YEAR(@Time2) AS VARCHAR) + '-' + CAST(MONTH(@Time2) AS VARCHAR))
END
INTO #datepoints
Затем выберите порядковое расстояние в единицах «гггг-мм», меньше единицы, чтобы преобразовать в кардинальное расстояние (то есть отрежьте кусок ткани от всей ткани в указанных точках разреза и получите его необработанное измерение):
SELECT
d.*,
Months_Between = (SELECT (SUM(l.value) - 1) FROM #linear_months l
WHERE l.[Linear_YearMonths] BETWEEN d.[YYYY-MM of Time1] AND d.[YYYY-MM of Time2])
FROM #datepoints d
Сырье:
Я называю это «необработанным расстоянием», потому что компонент месяца кардинального расстояния «гггг-мм» может быть слишком большим; Компоненты дневного цикла в месяце необходимо сравнить, чтобы увидеть, должно ли учитываться это значение за последний месяц. В частности, в этом примере необработанное выходное расстояние равно 12. Но это неправильно, так как 12/14 предшествует 12/15, поэтому, следовательно, прошло только 11 полных месяцев - это всего один день до 12-го месяца. Поэтому мы должны ввести внутримесячный дневной цикл, чтобы получить окончательный ответ. Вставьте сравнение положения «месяц, день» между, чтобы определить, считается ли месяц последней точки даты номинальным или нет:
SELECT
d.*,
Months_Between = (SELECT (SUM(l.value) - 1) FROM AZ_VBP.[MY].[edg_Linear_YearMonths] l
WHERE l.[Linear_YearMonths] BETWEEN d.[YYYY-MM of Time1] AND d.[YYYY-MM of Time2])
+ (CASE WHEN DAY(Time1) < DAY(Time2)
THEN -1
ELSE 0
END)
FROM #datepoints d
Окончательный вывод:
Правильный ответ «11» теперь наш вывод. И так, я надеюсь, это поможет. Спасибо!