删除成对的事务

时间:2018-01-13 01:49:59

标签: sql sql-server-2008

我正在尝试删除已从表中撤消的事务。该表有帐户,日期,金额和行。如果交易已被撤销,则账户将匹配,金额将相互反转。

示例表

Account    Date     Amount   Row
12         1/1/18   45       72    -- Case 1
12         1/2/18   50       73
12         1/2/18  -50       74
12         1/3/18   52       75

15         1/1/18   51       76    -- Case 2
15         1/2/18   51       77
15         1/2/18  -51       78
15         1/2/18   51       79

18         1/2/18   50       80    -- Case 3
18         1/2/18   50       81
18         1/2/18  -50       82
18         1/2/18  -50       83
18         1/3/18   50       84
18         1/3/18   50       85

20         1/1/18   57       88    -- Case 4
20         1/2/18   57       89
20         1/4/18  -57       90
20         1/5/18   57       91

期望的结果表

Account    Date     Amount   Row
12         1/1/18   45       72    -- Case 1
12         1/3/18   52       75

15         1/1/18   51       76    -- Case 2
15         1/2/18   51       79

18         1/3/18   50       84    -- Case 3
18         1/3/18   50       85

20         1/1/18   57       88    -- Case 4
20         1/5/18   57       91

当所有其他列相同时存在多个事务时,删除所有反向事务的实例都不起作用。我的尝试是计算所有重复的事务,计算所有反向重复事务,减去那些以获得每个事务组所需的行数。我打算拉出前X行,但在大多数情况下我发现我想要每组的最后X行,甚至是混合(在案例2中的第一行和最后一行)。

我要么需要一种从原始表中删除对的方法,或者从我到目前为止的方法开始工作,这是一种区分要拉动的事务的方法。

到目前为止

代码:

--adding row Numbers
with a as (
select
account a,
date d,
amount f,
row_number() over(order by account, date) r
from table),

--counting Duplicates  
b as (
select a.a, a.f, Dups
from a join (
    select a, f, count(*) Dups
    from a
    group by a.a, a.f
    having count(*)>1
    ) b
on a.a=b.a and
b.f=a.f
where a.f>0
),

--counting inverse duplicates
c as (
select a.a, a.f, InvDups
from a join (
    select a, f, count(*) InvDups
    from a
    group by a.a, a.f
    having count(*)>1
    ) b
on a.a=b.a and
-b.f=a.f
where a.f>0
),

--combining c and d to get desired number of rows of each transaction group
d as (
select
b.a, b.f, dups, InvDups, Dups-InvDups TotalDups
from b join c
on b.a=c.a and
b.f=c.f
),

--getting the number of rows from the beginning of each transaction group
select d.a, d.d, d.f
from
    (select
    a, d, f, row_number() over (group by a, d, f) r2
    from a) e
join d 
on e.a=d.a and
TotalDups<=r2

3 个答案:

答案 0 :(得分:1)

你可以试试这个。

SELECT T_P.* FROM 
    ( SELECT *, ROW_NUMBER() OVER(PARTITION BY Account, Amount ORDER BY [Row] ) RN from @MyTable WHere Amount > 0 ) T_P
    LEFT JOIN
    ( SELECT *, ROW_NUMBER() OVER(PARTITION BY Account, Amount ORDER BY [Row] ) RN from @MyTable WHere Amount < 0 ) T_N
ON T_P.Account = T_N.Account
    AND T_P.Amount = ABS(T_N.Amount)
    AND T_P.RN = T_N.RN
WHERE 
    T_N.Account IS NULL

答案 1 :(得分:0)

以下处理您的三种情况:

with t as (
      select t.*,
             row_number() over (partition by account, date, amount order by row) as seqnum
      from table t
     )
select t.*
from t
where not exists (select 1
                  from t t2
                  where t2.account = t.account and t2.date = t.date and
                        t2.amount = -t.amount and t2.seqnum = t.seqnum
                 );

答案 2 :(得分:0)

使用此

;WITH CTE
AS
(
    SELECT
        [Row]
        FROM YourTable YT
            WHERE Amount > 0
                AND EXISTS
                (
                    SELECT 1 FROM YourTable WHERE Account = YT.Account
                            AND [Date] = YT.[Date] 
                            AND (Amount+YT.Amount)=0
                )
    UNION ALL   
    SELECT
        [Row]
        FROM YourTable YT
            WHERE Amount < 0
                AND EXISTS
                (
                    SELECT 1 FROM YourTable WHERE Account = YT.Account
                            AND [Date] = YT.[Date] 
                            AND (Amount+YT.Amount)>0
                )
)
SELECT * FROM YourTable
    WHERE EXISTS
    (
        SELECT 1 FROM CTE WHERE [Row] = YourTable.[Row]
    )