1

给定一些类似于以下临时表中的行:

TransactionId | AccountsDocumentLineId | Amount
-------------   ----------------------   ------
52345           12345                    -15.79
52346           12345                    15.79
52347           12345                    -15.79
52348           22222                    -6.34
52349           22222                    6.34
52350           22222                    6.34
52351           22222                    -6.34
52352           22222                    -8.76
52353           22222                    10.49

如何确保删除任何相互抵消(总和为零)的 AccountsDocumentLineId,并且只留下那些没有更多匹配行可以配对的行?

因此,在上面的示例中,带有TransactionId= 52345( 52347) 的行将被保留(因为-15.79and15.79会相互抵消)。

类似地,带有TransactionId=52352和的52353行将保留(因为两对-6.346.34将相互抵消)。

因此,我们将得到以下最终结果:

TransactionId | AccountsDocumentLineId | Amount
-------------   ----------------------   ------
52347           12345                    -15.79
52352           22222                    -8.76
52353           22222                    10.49

注意:我已经删除了所有不必要的细节(更多的列和更多的行)来简单地展示手头的问题。

我玩过的一些选项是: -

  • 总结Amount一组 s 中的所有AccountDocumentLineIds 并查看哪一行与余额匹配,但这只会处理第一个示例(只剩下一行),而不是需要保留两行的第二个示例(所以不容易拆分一个值来给出两行)

  • 遍历一组AccountDocumentLineIds 中的每个条目,如果找到匹配项,则删除两个对应项;我认为这个会起作用,尽管不确定如何在 SQL 中进行操作?

SQLFiddle 演示

更新:根据Bulat 的回答添加了完整的答案。

4

3 回答 3

1

第一步很简单

DELETE FROM MYTABLE WHERE AccountsDocumentLineId IN
  (SELECT AccountsDocumentLineId from MYTABLE 
   GROUP BY AccountsDocumentLineId 
   HAVING SUM(Amount) <> 0)

但是你可能会发现你需要做一些调整来应对精度错误。

那应该只剩下不平衡的帐户。从那里你将不得不(我认为)创建一个存储过程来将借方“分配”到贷方。从您的数据样本看来,事务 id 的排序很可能是有序的,所以这应该会有所帮助。

于 2013-01-25T11:30:51.320 回答
1

您可以多次运行此代码(例如,当它影响超过 0 条记录时):

WITH Matches AS
(
SELECT t1 = t1. TransactionId, t2 = t2.TransactionId, 
  a1 = t1.Amount, a2 = t2.Amount, t1.AccountsDocumentLineId
FROM  Transactions t1 
    INNER JOIN Transactions t2 
        ON t1.AccountsDocumentLineId = t2.AccountsDocumentLineId
           AND t1.Amount = -t2.Amount 
           AND t1.TransactionId < t2.TransactionId 
)
DELETE FROM Transactions
WHERE EXISTS (
SELECT * FROM Matches m1
WHERE Transactions.TransactionId IN(m1.t1,m1.t2)
  AND NOT EXISTS
 (SELECT * FROM Matches m2
  WHERE  abs(m1.a1) = abs(m2.a1) 
     AND m1.AccountsDocumentLineId = m2.AccountsDocumentLineId
     AND (
         (m2.t1 > m1.t1 AND m2.t2 <= m1.t2)  
      OR (m2.t2 < m1.t2 AND (m2.t1 >= m1.t1))
      OR m2.t1 = m1.t2
     ) 
 )
);
于 2013-01-25T15:42:20.360 回答
0

为了响应 Bulat 对我的打包代码的请求,我在此处提交它(为了他人的利益):

DECLARE 
      @SequenceStart INT = 0,
      @SequenceEnd INT = 10 -- Just to be on the safe side, though unlikely for there to be 10 matching pairs for the same AccountsDocumentLineId

STARTLOOP:
    SET
        @SequenceStart = @SequenceStart+1   
    ;
    WITH Matches AS
    (
        SELECT
            L1 = L1.TransactionId, L2 = L2.TransactionId,
            A1 = L1.Amount, A2 = L2.Amount, L1.AccountsDocumentLineId
        FROM
            #RemainingLedgerEntries L1
            INNER JOIN #RemainingLedgerEntries L2
                ON L1.AccountsDocumentLineId = L2.AccountsDocumentLineId
                AND L1.Amount*-1 = L2.Amount
                AND L1.TransactionId < L2.TransactionId
    )
    DELETE FROM
        #RemainingLedgerEntries
    WHERE
        EXISTS
        (
            SELECT
                *
            FROM
                Matches M1
            WHERE
                #RemainingLedgerEntries.TransactionId IN (M1.L1, M1.L2)
            AND NOT EXISTS
            (
                SELECT
                    *
                FROM
                    Matches M2
                WHERE
                    ABS(M1.A1) = ABS(M2.A1) 
                AND M1.AccountsDocumentLineId = M2.AccountsDocumentLineId
                AND
                (
                    (M2.L1 > M1.L1 AND M2.L2 <= M1.L2)  
                    OR (M2.L2 < M1.L2 AND (M2.L1 >= M1.L1))
                    OR M2.L1 = M1.L2
                ) 
            )
        )

    IF @SequenceStart >= @SequenceEnd
        GOTO ENDTASK

    GOTO STARTLOOP

ENDTASK:
于 2013-01-29T11:12:31.193 回答