给定一些类似于以下临时表中的行:
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.79
并且15.79
会互相取消。)
同样,TransactionId
= 52352
和52353
的行仍将保留(因为两对-6.34
和6.34
会相互抵消)
因此,我们会得到以下最终结果:
TransactionId | AccountsDocumentLineId | Amount
------------- ---------------------- ------
52347 12345 -15.79
52352 22222 -8.76
52353 22222 10.49
注意:我已经删除了所有不必要的细节(更多列和更多行)以简单地显示手头的问题。
我玩过的一些选项是: -
总结一组Amount
内的所有AccountDocumentLineId
并查看哪一行与余额匹配,但这只会处理第一个示例(仅限于那里)剩下一行)和不第二个有两行需要保留(所以不容易拆分一个值来给两行)
浏览AccountDocumentLineId
组中的每个条目,如果找到匹配项,请删除两个对应项; 我认为这个可行吗虽然不确定如何在SQL中执行此操作?
更新:根据complete answer添加了Bulat's answer。
答案 0 :(得分:1)
第一步很简单
DELETE FROM MYTABLE WHERE AccountsDocumentLineId IN
(SELECT AccountsDocumentLineId from MYTABLE
GROUP BY AccountsDocumentLineId
HAVING SUM(Amount) <> 0)
但是你可能会发现你有一些需要做的事情来应对精确错误。
这应该只留给你不平衡的账户。从那里你必须(我认为)创建一个存储过程来“分配”到信用的借记。从您的数据示例中可能看出事务ID很有条理,因此应该有所帮助。
答案 1 :(得分: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
)
)
);
答案 2 :(得分: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: