2

给我 2 个日期范围(以前的日期和当前日期),我想比较这 2 个日期范围的值。

比较方法:

  • 一个日期范围的第 n 天值与另一个日期范围的第 n 天值比较(例如,前一个日期范围的第 1 天的值将与当前日期范围的第 1 天的值进行比较,依此类推)
  • 如果表格中不存在日期,则显示该值为零。
  • 如果 2 个日期范围的长度不同,则显示 NULL 和零以填补空白。

例子

数据库表“数据”:

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
4

2 回答 2

1

当您询问时,最好将所有 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;
于 2018-05-25T11:59:50.587 回答
1

我要做的第一件事是为您的日期范围创建公用表表达式,每个表达式都有两列:一列包含数据值,一列包含行号值。这样,通过第 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);

现在,我已将您的@currentStartDateTimefrom更改2018-02-042018-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'

现在,我的解决方案似乎很麻烦,因为我想详细展示所有步骤。你也许可以简化它。

首先,计算以天为单位的最大日期差异。
然后,创建一个从 1 到该差值 + 1 的数字cte。
然后,为每个范围创建日历 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 上看到现场演示。

于 2018-05-25T12:28:51.383 回答