Сравните данные между двумя диапазонами дат в SQL Server - PullRequest
0 голосов
/ 25 мая 2018

Мне дано 2 диапазона дат (предыдущие и текущие даты), и я хочу сравнить значения этих 2 диапазонов дат.

Метод сравнения:

  • Значение n-го дня одного диапазона дат по сравнению со значением n-го дня другого диапазона дат (например, значение 1-го дня предыдущего диапазона дат будетпо сравнению со значением 1-го дня текущего диапазона дат и т. д.)
  • Если дата не существует в таблице, отобразите ноль для значения.
  • Если 2 диапазона дат не находятся втой же длины, отобразите NULL и ноль, чтобы заполнить пробел.

Пример

БД Таблица "data":

date         | value
-------------|------
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

Данодиапазоны дат: (текущий) с 2018-02-04 по 2018-02-07, (предыдущий) с 2018-01-01 по 2018-01-03

Желаемый вывод:

curDate      | curValue | preDate     | preValue
-------------|----------|-------------|---------
2018-02-04   | 4        | 2018-01-01  | 3
2018-02-05   | 2        | 2018-01-02  | 5
2018-02-06   | 7        | 2018-01-03  | 8
2018-02-07   | 0        | NULL        | 0

Теперь я застрял в состоянии соединения, и мой текущий SQL выглядит так:

DECLARE @currentStartDateTime  datetime   = '2018-02-04 00:00:00'
DECLARE @currentEndDateTime    datetime   = '2018-02-07 23:59:59'
DECLARE @previousStartDateTime datetime   = '2018-01-01 00:00:00'
DECLARE @previousEndDateTime   datetime   = '2018-01-03 23:59:59'

SELECT   cur.[date]             AS [curDate]
        ,ISNULL(cur.[total], 0) AS [curTotal]
        ,pre.[date]             AS [preDate]
        ,ISNULL(pre.[total], 0) AS [preTotal]
    FROM (
        SELECT * FROM [data] 
            WHERE [date] BETWEEN @currentStartDateTime AND @currentEndDateTime
    ) cur
    FULL OUTER JOIN (       
        SELECT * FROM [data] 
            WHERE [date] BETWEEN @previousStartDateTime AND @previousEndDateTime
    ) pre
        ON cur.[date] = DATEADD(day, 1, pre.[date]) -- <<< Stuck in this part

Ответы [ 2 ]

0 голосов
/ 25 мая 2018

Первое, что я хотел бы сделать, это создать общие табличные выражения для диапазонов дат, каждый из которых имеет два столбца: столбец со значением данных и столбец со значением номера строки.Таким образом, будет очень легко объединить по 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.

0 голосов
/ 25 мая 2018

Рекомендуется включать все DDL и вставки вместе, когда вы спрашиваете.

DROP TABLE data;
CREATE TABLE data
(
    date DATE,
    value INT
);
GO
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);

DECLARE @currentStartDateTime DATETIME = '2018-02-04 00:00:00';
DECLARE @currentEndDateTime DATETIME = '2018-02-07 23:59:59';
DECLARE @previousStartDateTime DATETIME = '2018-01-01 00:00:00';
DECLARE @previousEndDateTime DATETIME = '2018-01-03 23:59:59';

SELECT a.date curDate,
       a.value curValue,
       b.date preDate,
       COALESCE(b.value, 0) preValue
FROM
(
    SELECT *,
           ROW_NUMBER() OVER (ORDER BY date) rn
    FROM data
    WHERE date
    BETWEEN @currentStartDateTime AND @currentEndDateTime
) a
    LEFT JOIN
    (
        SELECT *,
               ROW_NUMBER() OVER (ORDER BY date) rn
        FROM data
        WHERE date
        BETWEEN @previousStartDateTime AND @previousEndDateTime
    ) b
        ON a.rn = b.rn;
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...