SQL - 特定行之间的值差异

时间:2016-09-13 14:57:16

标签: sql sql-server tsql

我的查询如下

SELECT 
    LEFT(TimePeriod,6) Period, -- string field with YYYYMMDD
    SUM(Value) Value
FROM 
    f_Trans_GL
WHERE 
    Account = 228
GROUP BY 
    TimePeriod

它返回

Period   Value
---------------
201412    80
201501    20
201502    30
201506    50
201509   100
201509   100

我想知道相隔1个月的行之间的价值差异。计算为[值期间] - [值期间-1]。

所需的输出是;

Period   Value   Calculated
-----------------------------------
201412    80      80 - null = 80
201501    20      20 - 80 = -60
201502    30      30 - 20 = 10
201506    50      50 - null = 50
201509   100     (100 + 100) - null = 200

这说明了第二个挑战,因为如果年份发生变化(201501和201412之间的差异为一个月),则需要评估期间。

第三个挑战是重复期(201509),在这种情况下需要评估该期间的总和。

如果可能的话,任何开始的指标都会很棒!

提前致谢

===============================

在我接受了答案后,我根据自己的需要量身定制了一点,最终结果是:

WITH cte
AS (SELECT

  ISNULL(CAST(TransactionID AS nvarchar), '_nullTransactionId_') + ISNULL(Description, '_nullDescription_') + CAST(Account AS nvarchar) + Category + Currency + Entity + Scenario AS UID,

  LEFT(TimePeriod, 6) Period,
  SUM(Value1) Value1,
  CAST(LEFT(TimePeriod, 6) + '01' AS date) ord_date
FROM MyTestTable
GROUP BY LEFT(TimePeriod, 6),
         TransactionID,
         Description,
         Account,
         Category,
         Currency,
         Entity,
         Scenario,
         TimePeriod)
SELECT
  a.UID,
  a.Period,
  --a.Value1,
  ISNULL(a.Value1, 0) - ISNULL(b.Value1, 0) Periodic
FROM cte a
LEFT JOIN cte b
  ON a.ord_date = DATEADD(MONTH, 1, b.ord_date)
ORDER BY a.UID

我必须为每个UID获取新值(Periodic)。此UID必须在此处确定,因为表中的PK将不起作用。

但问题是,这将返回比我实际在表中开始的更多行。如果我没有按UID添加GROUP BY和ORDER(如上所述),我可以告诉UID和Period的每个组合的第一个结果实际上是正确的,该组合的后续行不是。

我不知道在哪里寻找解决方案,我的猜测是UID就是这里的问题,并且它将以某种方式迭代场......任何方向都值得赞赏。

3 个答案:

答案 0 :(得分:1)

正如其他人指出的那样,第一个错误是Group by你需要Left(timeperiod, 6)而不是timeperiod

对于剩余计算,尝试类似这样的

;WITH cte 
     AS (SELECT LEFT(timeperiod, 6)                      Period, 
                Sum(value)                               Value, 
                Cast(LEFT(timeperiod, 6) + '01' AS DATE) ord_date 
         FROM   f_trans_gl 
         WHERE  account = 228 
         GROUP  BY LEFT(timeperiod, 6)) 
SELECT a.period, 
       a.value, 
       a.value - Isnull(b.value, 0) 
FROM   cte a 
       LEFT JOIN cte b 
              ON a.ord_date = Dateadd(month, 1, b.ord_date) 

如果您使用SQL SERVER 2012,则可以使用LAG分析函数轻松完成此操作

答案 1 :(得分:1)

使用派生表,您可以将数据连接到自身以查找上一期间的行。我已将您的Period转换为Date值,以便您可以使用SQL Server的dateadd函数检查上个月的行:

;WITH cte AS
(
SELECT 
    LEFT(TimePeriod,6) Period, -- string field with YYYYMMDD
    CAST(TimePeriod + '01' AS DATE) PeriodDate
    SUM(Value) Value
FROM f_Trans_GL
WHERE Account = 228
GROUP BY LEFT(TimePeriod,6)
)
SELECT c1.Period,
    c1.Value,
    c1.Value - ISNULL(c2.Value,0) AS Calculation
FROM cte c1
    LEFT JOIN cte c2
        ON c1.PeriodDate = DATEADD(m,1,c2.PeriodDate)

答案 2 :(得分:0)

没有cte,你也可以尝试这样的事情

SELECT A.Period,A.Value,A.Value-ISNULL(B.Value) Calculated
FROM
(
    SELECT LEFT(TimePeriod,6) Period
    DATEADD(M,-1,(CONVERT(date,LEFT(TimePeriod,6)+'01'))) PeriodDatePrev,SUM(Value) Value
    FROM f_Trans_GL
    WHERE Account = 228
    GROUP BY LEFT(TimePeriod,6)
) AS A
LEFT OUTER JOIN
(
    SELECT LEFT(TimePeriod,6) Period
    (CONVERT(date,LEFT(TimePeriod,6)+'01')) PeriodDate,SUM(Value) Value
    FROM f_Trans_GL
    WHERE Account = 228
    GROUP BY LEFT(TimePeriod,6)
) AS B
ON (A.PeriodDatePrev = B.PeriodDate)
ORDER BY 1