1

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

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

例子:

在这张桌子上:

车牌 ​​| 日期 | 里程
--------------------------
    1 1/1/2000 10000
    1 2000 年 2 月 1 日 11000
    1 2000 年 3 月 1 日 12000
    2 2000 年 1 月 1 日 10000
    2 2000 年 2 月 1 日 11001
    2 2000 年 3 月 1 日 12001
    3 2000 年 1 月 1 日 10000
   (缺少 (3, 2/1/2000) 的数据点)
    3 2000 年 3 月 1 日 12000

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

车牌 ​​| 日期 | 三角洲
--------------------------
    1 1/1/2000 10000
    1 2000 年 2 月 1 日 1000
    1 3/1/2000 1000
    2 2000 年 1 月 1 日 10000
    2 2000 年 2 月 1 日 1001
    2 2000 年 3 月 1 日 1000
    3 2000 年 1 月 1 日 10000
    3 2000 年 3 月 1 日 2000 年
4

4 回答 4

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小提琴

注意:这假设“缺少 (3, 2/1/2000) 的数据点”意味着表中没有汽车 3 的行,2000 年 2 月。

于 2013-02-12T19:52:28.943 回答
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
于 2013-02-12T20:04:25.087 回答
1

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

这在某些限制范围内有效 - 即 car#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)
于 2013-02-12T20:54:30.347 回答
1

Same approach as the one from @Richard Deeming, but this one regards possible null values as included in original question.

;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

See SQL-Fiddle.

于 2013-02-12T22:48:02.317 回答