数据值变化的查询(持续时间)不同

dsu*_*sum 4 sql-server-2008 sql-server

我正在尝试编写一个查询来获取表中状态的持续时间。此查询需要在 SQL Server 2008 中工作。

假设我有下表:

Key Value   RecordDate
1   1   2012-01-01
2   1   2012-01-02
3   1   2012-01-03
4   5   2012-01-05
5   5   2012-01-05 12:00:00
6   12  2012-01-06
7   1   2012-01-07
8   1   2012-01-08
Run Code Online (Sandbox Code Playgroud)

我想得到以下结果

Value StartDate   EndDate     Duration
1     2012-01-01  2012-01-05  4 days
5     2012-01-05  2012-01-06  1 days
12    2012-01-06  2012-01-07  1 days
1     2012-01-07  NULL        NULL
Run Code Online (Sandbox Code Playgroud)

基本上我想在它改变之前获取值的持续时间。

我正在接近某个地方,但仍然无法弄清楚:

SELECT [Key], [Value],  
       MIN(RecordDate) OVER(PARTITION BY [Value]) as 'StarDate',
       MAX(RecordDate) OVER(PARTITION BY [Value]) as 'EndDate',
       DATEDIFF(day, (MIN(RecordDate) OVER(PARTITION BY [Value])), 
                     (MAX(RecordDate) OVER(PARTITION BY [Value])))
FROM [RateTable]
Order by RecordDate
Run Code Online (Sandbox Code Playgroud)

我知道,SQL Server 2012中具有LAGLEAD功能,但由于我处理SQL Server 2008中,我无法使用它。

请指教

这是生成示例数据的 SQL 语句

CREATE TABLE RateTable(
    [Key] [int] IDENTITY(1,1) NOT NULL,
    [Value] [int] NULL,
    [RecordDate] [DateTime] NULL
    )
GO

INSERT INTO [RateTable] VALUES (1, '2012-01-01');
INSERT INTO [RateTable] VALUES (1, '2012-01-02');
INSERT INTO [RateTable] VALUES (1, '2012-01-03');
INSERT INTO [RateTable] VALUES (5, '2012-01-04');
INSERT INTO [RateTable] VALUES (5, '2012-01-05 12:00:00');
INSERT INTO [RateTable] VALUES (12, '2012-01-06');
INSERT INTO [RateTable] VALUES (1, '2012-01-07');
INSERT INTO [RateTable] VALUES (1, '2012-01-08');
GO
Run Code Online (Sandbox Code Playgroud)

[更新] 感谢大家的投入。我想在这里澄清一些问题,b/c 我在这里简化了很多事情,这样我就不会给我正在处理的问题增加额外的复杂性。背景是我有一个表格,记录了大量车辆的故障灯指示 (MIL) 状态。此 MIL 状态信息会根据多种因素不定期记录。我想得到的是车辆 MIL 状态开启时的持续时间(值为 1)。该值可以是 -1(未定义)或 0(关闭)。

  1. RecordDate 几乎可以保证是唯一的,它应该是 DateTime 类型(对不起,我第一次发布这个问题时没有正确的数据类型)
  2. 由于它应该是日期时间,因此您不必担心每天有多个值
  3. RecordDate 和 Value 不可为 Null
  4. 价值变化的持续时间是挑战。不能像你们中的一些人提到的那样真正使用 Group By 子句,b/c 我需要知道 MIL 的状态何时发生变化,而不是分组值的最后记录日期。
  5. 该表有移动者 1600 万条记录,b/c 是一个日志表。

最初当我发布这个问题时,我认为有简单的 sql 语句解决方案,而不是使用带有 Cursor 的 T-SQL 函数......如果我可以使用 sql 语句 b/c 它与其他第三方集成有很多优点更好地报告应用程序。如果没有 SQL 2012 分析函数,这看起来很难做到。即使有 SQL 语句解决方案,我现在也担心它会太复杂而无法理解和维护。

【更新2】希望还是有人提供基于sql查询的解决方案。但是,由于我的项目时间限制,我现在编写了一个函数来解决这个问题。

CREATE Function GetRateReport (@StartDate DateTime,  @EndDate DateTime)
RETURNS @ResultSet TABLE
(
    [RecordDate] DateTime,
    [Value] int,
    [Duration] DateTime
) AS
BEGIN

    DECLARE @PreviousValue INT;
    DECLARE @EarliestRecordDate DateTime;
    DECLARE @CurrentValue INT;
    DECLARE @CurrentRecordDate DateTime;

    --Open a cursor to get source data
    DECLARE sourceCursor CURSOR LOCAL STATIC READ_ONLY FORWARD_ONLY
    FOR
    SELECT RecordDate, Value
    FROM   RateTable 
    WHERE RecordDate BETWEEN @StartDate AND @EndDate
    ORDER BY RecordDate;

    --Get the first record
    OPEN sourceCursor 
    FETCH NEXT FROM sourceCursor INTO @CurrentRecordDate, @CurrentValue;

    --Initize value
    SET @EarliestRecordDate = @CurrentRecordDate;
    SET @PreviousValue = @CurrentValue;

    WHILE(@@Fetch_Status = 0) -- check for more row
    BEGIN

        -- Insert result when value changed
        IF @CurrentValue <> @PreviousValue
        BEGIN
            INSERT INTO @ResultSet(RecordDate, Value, Duration)
            VALUES (@EarliestRecordDate, @PreviousValue, 
                      @CurrentRecordDate - @EarliestRecordDate);

            SET @EarliestRecordDate = @CurrentRecordDate;
            SET @PreviousValue = @CurrentValue;
        END

    FETCH NEXT FROM sourceCursor INTO @CurrentRecordDate, @CurrentValue;
    END

    --Edge case, retrieve the last value with duration up to current date
    INSERT INTO @ResultSet(RecordDate, Value, Duration)
    VALUES (@CurrentRecordDate, @CurrentValue, GETDATE() - @CurrentRecordDate);

    RETURN
END
GO

--Example
Select [RecordDate], [Value], 
  DateDiff(day, '1900-01-01', [Duration]) as 'Day Duration' 
  from GetRateReport('2012-01-01', '2012-01-31')
GO
Run Code Online (Sandbox Code Playgroud)

Jon*_*gel 7

此解决方案假定Value每个RecordDate. 使用调优的索引性能会更好,并且可能会使用临时表或表变量破坏进程。我将您的脚本用于测试数据。输出可能不是您想要的那样,但让您接近的过程很重要 - 它的工作原理是将行过滤为仅开始间隔的行,然后通过从结果中偏移结果来制作结束日期前一步。

WITH a AS
(
    SELECT
        Value,
        RecordDate,
        ROW_NUMBER() OVER(ORDER BY RecordDate) AS RN
        FROM [dbo].[RateTable] rt
),
b AS
(
    SELECT
        a1.Value,
        a1.RecordDate,
        ROW_NUMBER() OVER(ORDER BY a1.RecordDate) AS RN
        FROM a a1
        LEFT OUTER JOIN a a2 ON a2.RN = a1.RN - 1
        WHERE
            (a1.Value != a2.Value) OR
            (a2.RN IS NULL)
)
SELECT
    b1.Value,
    b1.RecordDate AS StartDate,
    b2.RecordDate AS EndDate,
    DATEDIFF(DAY, b1.RecordDate, b2.RecordDate) + 1 AS Duration /* Fixme? */
    FROM b b1
    LEFT OUTER JOIN b b2 ON b2.RN = b1.RN + 1
    ORDER BY b1.RecordDate;
Run Code Online (Sandbox Code Playgroud)

输出:

值 StartDate EndDate 持续时间
1 2012-01-01 00:00:00.000 2012-01-04 00:00:00.000 4
5 2012-01-04 00:00:00.000 2012-01-06 00:00:00.000 3
12 2012-01-06 00:00:00.000 2012-01-07 00:00:00.000 2
1 2012-01-07 00:00:00.000 NULL NULL