计算SQL中的累积值?

时间:2013-02-12 19:47:25

标签: sql tsql

在不同日期(每月的第一天)给出一张汽车及其里程表读数表,如何编写TSQL(理想情况下,用作SQL Server视图)以返回“增量”值?

换句话说,我希望从Calculate a Running Total in SQL Server进行反向操作。

示例:

在这张桌子上:

CarId |   Date    | Mileage
---------------------------
    1    1/1/2000    10000
    1    2/1/2000    11000
    1    3/1/2000    12000
    2    1/1/2000    10000
    2    2/1/2000    11001
    2    3/1/2000    12001
    3    1/1/2000    10000
   (missing datapoint for (3, 2/1/2000))
    3    3/1/2000    12000

我们会返回类似的内容(细节/边缘情况很灵活):

CarId |   Date    | Delta
---------------------------
    1    1/1/2000    10000
    1    2/1/2000    1000
    1    3/1/2000    1000
    2    1/1/2000    10000
    2    2/1/2000    1001
    2    3/1/2000    1000
    3    1/1/2000    10000
    3    3/1/2000    2000

4 个答案:

答案 0 :(得分:2)

这适用于SQL 2005或更高版本:

WITH cteData As
(
   SELECT
      CarId,
      Date,
      Mileage,
      ROW_NUMBER() OVER (PARTITION BY CarId ORDER BY Date) As RowNumber
   FROM
      dbo.Cars
)
SELECT
   C.CarId,
   C.Date,
   CASE
      WHEN P.CarId Is Null THEN C.Mileage
      ELSE C.Mileage - P.Mileage
   END As Delta
FROM
   cteData As C
   LEFT JOIN cteData As P
   ON P.CarId = C.CarId
   And P.RowNumber = C.RowNumber - 1
ORDER BY
   C.CarId,
   C.Date
;

SQL Fiddle

NB:这假定“缺少(3,2 / 1/2000)的数据点”表示2000年2月的汽车3表中没有行。

答案 1 :(得分:1)

窗口功能很棒。但是SQL Server在SQL Server 2012之前没有你需要的那个。在那里,你有滞后函数:

select t.*,
       (Milage - lag(Milage) over (partition by carId order by date)) as Delta
from t

对于早期版本,您可以使用相关子查询:

[麻烦上传查询],唉。

    select t.*, (Mileage - prevMileage) as Delta
    from (select t.*,     
                 (select top 1 Mileage    from t t2
                  where t2.carId = t.carId and t2.date < t.date order by desc
                ) as prevDelta 
      from t
     ) t

答案 2 :(得分:1)

尝试在不依赖于任何2012函数,游标,while循环等的情况下执行此操作

这可以在一些限制范围内运行 - 也就是说,汽车#3的入口空值是一个问题:

DECLARE @cars table ([id] int, [date] smalldatetime, [mileage] int)
INSERT INTO @cars ([id], [date], [mileage])
SELECT 1, '1/1/2000', 10000 UNION ALL
SELECT 1, '2/1/2000', 11000 UNION ALL
SELECT 1, '3/1/2000', 12000 UNION ALL
SELECT 2, '1/1/2000', 10000 UNION ALL
SELECT 2, '2/1/2000', 11000 UNION ALL
SELECT 2, '3/1/2000', 12000 UNION ALL
SELECT 3, '1/1/2000', 10000 UNION ALL
SELECT 3, '2/1/2000', NULL UNION ALL
SELECT 3, '3/1/2000', 12000


SELECT t1.id, t1.date, t1.mileage, t2.id, t2.date, t2.mileage, t1.mileage - t2.mileage as miles FROM @cars t1
LEFT JOIN @cars t2
ON t1.id = t2.id
AND t1.date = DATEADD(MONTH,1, t2.date)

答案 3 :(得分:1)

与@Richard Deeming的方法相同,但这个方法与原始问题中包含的可能的空值相关。

;with cte ( rn, id, date, mileage )
as
(
  select
     row_number() over ( partition by id order by id, date )
     , id
     , date
     , mileage
   from
      cars
   where
      mileage is not null
)
select
  "current".id
  , "current".date
  , delta = isnull( "current".mileage - predecessor.mileage, "current".mileage )
from
  cte as "current"
  left join cte as predecessor
    on "current".id = predecessor.id
    and "current".rn - 1 = predecessor.rn

请参阅SQL-Fiddle