Первое, что я хотел бы сделать, это создать общие табличные выражения для диапазонов дат, каждый из которых имеет два столбца: столбец со значением данных и столбец со значением номера строки.Таким образом, будет очень легко объединить по n-му значению.
Итак, вот мое предлагаемое решение:
Сначала создайте и заполните образец таблицы ( Пожалуйста сохраните этот шаг в ваших будущих вопросах)
DECLARE @data As Table
(
[date] DATE,
[value] INT
);
INSERT INTO @data
VALUES
('2018-01-01', 3),
('2018-01-02', 5),
('2018-01-03', 8),
('2018-01-04', 6),
('2018-02-04', 4),
('2018-02-05', 2),
('2018-02-06', 7),
('2018-02-07', 0);
Теперь я изменил ваш @currentStartDateTime
с 2018-02-04
на 2018-02-03
, чтобы я также возвращал строки, которых нет втаблица ( Пожалуйста, , убедитесь, что ваши образцы данных покрывают все запросы)
DECLARE @currentStartDateTime datetime = '2018-02-03 00:00:00',
@currentEndDateTime datetime = '2018-02-07 23:59:59',
@previousStartDateTime datetime = '2018-01-01 00:00:00',
@previousEndDateTime datetime = '2018-01-03 23:59:59'
Теперь мое решение кажется довольно обременительным, потому что я хотел показать все шаги в деталях.Возможно, вам удастся упростить его.
Сначала вычислите максимальную разницу дат в днях.
Затем создайте числа cte от 1 до этой разности + 1.
Затем создайте календарные ctes длякаждый диапазон,
Затем последний cte для полного объединения между диапазонами,
и выбор из этого последнего cte слева, присоединенный дважды к таблице данных.
-- This allows us to use the smallest possible tally cte.
DECLARE @MaxDateDiff int;
SELECT @MaxDateDiff = MAX(d)
FROM (
VALUES (DATEDIFF(DAY, @currentStartDateTime, @currentEndDateTime)),
(DATEDIFF(DAY, @previousStartDateTime, @previousEndDateTime))
) v(d) -- I like table value constructors :-)
;WITH Tally AS
(
SELECT TOP (@MaxDateDiff + 1) ROW_NUMBER() OVER (ORDER BY (SELECT NULL)) As Number
FROM sys.objects a
-- if your database is very small (number of tables, procedures ect'),
-- you might want to unremark the next row
-- CROSS JOIN sys.objects b
),
CurrentRange AS
(
SELECT DATEADD(DAY, Number-1, @currentStartDateTime) As [Date], Number
FROM Tally
-- we need the where clause in case the other range is bigger...
WHERE DATEADD(DAY, Number-1, @currentStartDateTime) <= @currentEndDateTime
),
PreviousRange AS
(
SELECT DATEADD(DAY, Number-1, @previousStartDateTime) As [Date], Number
FROM Tally
WHERE DATEADD(DAY, Number-1, @previousStartDateTime) <= @previousEndDateTime
),
BothRanges AS
(
SELECT C.Date As CurDate, P.Date As PreDate
FROM CurrentRange As C
FULL JOIN PreviousRange As P ON C.Number = P.Number
)
SELECT CurDate, ISNULL(c.Value, 0) as CurValue, PreDate, ISNULL(p.Value, 0) as PreValue
FROM BothRanges
LEFT JOIN @data AS c ON CurDate = c.[Date]
LEFT JOIN @data AS p ON PreDate = p.[Date]
Результаты: (Помнитечто @currentStartDateTime
отличается от указанного в вопросе)
CurDate CurValue PreDate PreValue
03.02.2018 00:00:00 0 01.01.2018 00:00:00 3
04.02.2018 00:00:00 4 02.01.2018 00:00:00 5
05.02.2018 00:00:00 2 03.01.2018 00:00:00 8
06.02.2018 00:00:00 7 NULL 0
07.02.2018 00:00:00 0 NULL 0
Вы можете увидеть живое демо на rextester.