根据它们之间的函数拉出两个特定的数据行

时间:2017-12-01 20:12:59

标签: sql tsql

我试图看到基于百分比的成本增加。数据看起来像这样

ID            Date           Amount
123           5/1/2017       500
123           6/1/2017       500
123           7/1/2017       500
123           8/1/2017       1200
123           9/1/2017       1200
456           5/1/2017       400
456           6/1/2017       450
456           7/1/2017       450
789           5/1/2017       600
789           6/1/2017       600
789           7/1/2017       900

我想要做的是找到金额增加预定金额(50%或500)并且我想拉出先前金额的记录和新金额。结果应如下所示

ID            Date           Amount
123           7/1/2017       500
123           8/1/2017       1200
789           6/1/2017       600
789           7/1/2017       900

有数百万行,日期可能会有所不同,因此无法进行小型临时表解决方案。

我不知道如何开始这样的事情。我正在使用TSQL

5 个答案:

答案 0 :(得分:1)

如果您的SQL Server版本支持laglead,请使用

select id,date,amount
from (
select t.*,
case when amount-lag(amount) over(partition by id order by date) >= 500 or 
          100.0*(amount-lag(amount) over(partition by id order by date))/lag(amount) over(partition by id order by date) >= 50 or  
          lead(amount) over(partition by id order by date)-amount >= 500 or 
          100.0*(lead(amount) over(partition by id order by date)-amount)/amount >= 50 
then 1 else 0 end as to_select 
from tbl t
) t
where to_select = 1

答案 1 :(得分:1)

这不优雅,但它应该让你去......

SELECT ID, MyDate, amount
FROM (
    SELECT 
      MAX(Amount) OVER(PARTITION BY ID ORDER BY MyDate ROWS BETWEEN 1 PRECEDING AND 1 PRECEDING) AS PrevAmount,
      MAX(Amount) OVER(PARTITION BY ID ORDER BY MyDate ROWS BETWEEN 1 FOLLOWING AND 1 FOLLOWING) AS NextAmount,
      amount,
      Id,
      MyDate
    FROM (    
      SELECT 123 AS Id, CURRENT_DATE AS MyDate, 500 AS amount UNION ALL
      SELECT 123 AS Id, CURRENT_DATE + INTERVAL '1' DAY AS MyDate, 1200 AS amount UNION ALL
      SELECT 123 AS Id, CURRENT_DATE + INTERVAL '2' DAY AS MyDate, 1600 AS amount
    ) src
) src
WHERE 
CASE 
    WHEN 
        (
            ((PrevAmount - Amount) *100) / PrevAmount >= 50 OR (Amount - PrevAmount) >= 500
        ) OR
        (
            ((Amount - NextAmount) *100) / Amount >= 50 OR (NextAmount- Amount) >= 500                  
        )
    THEN 'Y' 
    ELSE 'N' 
END = 'Y'

答案 2 :(得分:1)

对于SQL 2008版本(没有LAG和LEAD)。 您可以使用临时表而不是CTE,它可以提高工作效率。

DECLARE @table TABLE
(
    Id      INT,
    [Date]  DATETIME,
    Amount  INT
)
INSERT INTO @table(Id, [Date], Amount) VALUES
(123, '5/1/2017', 500),
(123, '6/1/2017', 500),
(123, '7/1/2017', 500),
(123, '8/1/2017', 1200),
(123, '9/1/2017', 1200),
(456, '5/1/2017', 400),
(456, '6/1/2017', 450),
(456, '7/1/2017', 450),
(789, '5/1/2017', 600),
(789, '6/1/2017', 600),
(789, '7/1/2017', 900)


;WITH cte AS
(
    SELECT
        ROW_NUMBER() OVER(PARTITION BY Id ORDER BY [Date]) AS RowNumber,
        *
    FROM @table
)
SELECT
    CASE WHEN i.number = 1 THEN t.Id1 ELSE t.Id2 END AS Id,
    CASE WHEN i.number = 1 THEN t.Date1 ELSE t.Date2 END AS [Date],
    CASE WHEN i.number = 1 THEN t.Amount1 ELSE t.Amount2 END AS Amount
FROM
(
    SELECT
        t1.Id AS Id1,
        t1.[Date] AS Date1,
        t1.Amount AS Amount1,
        t2.Id AS Id2,
        t2.[Date] AS Date2,
        t2.Amount AS Amount2
    FROM cte AS t1
    INNER JOIN cte AS t2 ON t2.Id = t1.Id AND t2.RowNumber = t1.RowNumber + 1
    WHERE t2.Amount >= t1.Amount * 1.5 OR t2.Amount >= t1.Amount + 500 -- either 50% or 500
) AS t
CROSS JOIN
(
    SELECT 1 AS number
    UNION ALL SELECT 2
) AS i
ORDER BY Id, [Date]

输出:

Id     Date                       Amount
123    2017-07-01 00:00:00.000    500
123    2017-08-01 00:00:00.000    1200
789    2017-06-01 00:00:00.000    600
789    2017-07-01 00:00:00.000    900

答案 3 :(得分:0)

您可以使用lead()lag()

select t.*
from (select t.*,
             lag(amount) over (partition by id order by date) as prev_amount,
             lead(amount) over (partition by id order by date) as next_amount
      from t
     ) t
where (amount - prev_amount) > 500 or
      (next_amount - amount) > 500 or
      amount > prev_amount * 1.5 or
      next_amount > amount * 1.5;

答案 4 :(得分:0)

不知道“百万行”:

DML:

select 
    t1.id , 
    t1.date, 
    t1.amount,
    t2.date                  as t2date, 
    t2.amount                as t2amount,
    abs(t1.amount-t2.amount) as changeTotal, -- you might want to keep the sign
    (abs(t1.amount-t2.amount) / (t1.amount / 100.0)) 
                             as changePercent 

from TT as t1
join TT as t2 on t1.id = t2.id 

where t1.date < t2.date -- only join lesser to greater
and  not exists(        -- only if directly following each other
    select 1 
    from tt as o 
    where o.id = t1.id 
    and o.date > t1.date 
    and o.date < t2.date 
) -- output only if there is no other one between t1 and t2 

输出(手动剥离空白时间):

id      date           amount      t2date         t2amount    changeTotal     changePercent
123     2017-05-01     500         2017-06-01     500         0               0
123     2017-06-01     500         2017-07-01     500         0               0
123     2017-07-01     500         2017-08-01     1200        700             140
123     2017-08-01     1200        2017-09-01     1200        0               0
456     2017-05-01     400         2017-06-01     450         50              12.5
456     2017-06-01     450         2017-07-01     450         0               0
789     2017-05-01     600         2017-06-01     600         0               0
789     2017-06-01     600         2017-07-01     900         300             50

DDL:

CREATE TABLE TT     ("ID" int, "Date" datetime, "Amount" int);

INSERT INTO TT
    (ID, Date, Amount)
VALUES
    (123, '2017-05-01 00:00:00', 500),
    (123, '2017-06-01 00:00:00', 500),
    (123, '2017-07-01 00:00:00', 500),
    (123, '2017-08-01 00:00:00', 1200),
    (123, '2017-09-01 00:00:00', 1200),
    (456, '2017-05-01 00:00:00', 400),
    (456, '2017-06-01 00:00:00', 450),
    (456, '2017-07-01 00:00:00', 450),
    (789, '2017-05-01 00:00:00', 600),
    (789, '2017-06-01 00:00:00', 600),
    (789, '2017-07-01 00:00:00', 900)
;