删除给定键的总和为零的行
Remove Rows That Sum Zero For A Given Key
我有一个查询会导致在我们的 SSRS 2008 R2 服务器上创建客户账单。 SQL 服务器实例也是 2008 R2。查询很大,出于安全等原因,我不想 post 整个查询。
我需要对下面的示例数据做的是从结果集中删除 73.19 和 -73.19 的两行。因此,如果两行在 LineBalance 列中具有相同的绝对值并且它们的总和为 0 并且如果它们在 REF1 列中具有相同的值,则应从结果集中删除。结果集中仍应返回 REF1 = 14598 和行余额为 281.47 的行,不应返回下面 REF1 = 14598 的其他两行。
这里的重点是 "hide" 会计错误和客户的更正。 "hide" 我的意思是,不要在他们收到的邮件账单上显示。这里发生的事情是客户在本应在 281.47 开具账单时错误地在 73.19 开具账单。所以,我们的 AR 部门。将 73.19 退回他们的帐户并向他们收取正确金额 281.47。如您所见,它们都具有相同的 REF1 值。
它很笨重,但它可以让您识别匹配的负面违规者。您需要从结果集中排除两列中的 sysInvNum
Create table #tmp( SysInvNum int,Ref1 int, LineBalance decimal(8,2))
insert into #tmp
values(3344299,14602,558.83)
,(3344298,14598,281.47)
,(3344297,14602,-95.98)
,(3344296,14598,-73.19)
,(3341758,14598,73.19)
--Select * From #tmp
Select *
INTO #t1
From #tmp
Where LineBalance < 0
--Select * From #t1
Select SysInvNum1 = #tmp.SysInvNum, SysInvNum2 = #T1.SysInvNum
INTO #T2
From #tmp
LEFT JOIN #t1 On #tmp.Ref1 = #T1.Ref1 and #tmp.LineBalance = -#T1.LineBalance
where #t1.SysInvNum is not null
Select * From
#tmp
Where SysInvNum not in(
Select SysInvNum1 from #t2
union Select SysInvNum2 from #t2
)
drop table #tmp
drop table #t1
drop table #t2
此解决方案仅适用于 SQL Server 2012,但如果将来有人必须做类似的事情,我决定将其保留在这里,因为它非常简单.
.....
如果总和为 0 并且它们之间没有其他交易(负交易发生在正交易之后),这应该只删除特定收件人的两笔交易。比较保守和安全。
应该有关于交易顺序的信息。类似日期列的东西。在 PARTITION BY 语句中,您应该按此列而不是 PRIMARY KEY 列进行排序。
IF OBJECT_ID('Receipts', 'U') IS NOT NULL
DROP TABLE Receipts
CREATE TABLE Receipts
(
SysInvNum INT PRIMARY KEY IDENTITY(1,1),
REF1 INT,
LineBalance DECIMAL(10,2)
)
INSERT INTO Receipts
values
(14602,558.83),
(14598,281.47),
(14602,-95.98),
(14598,73.19),
(14598,-73.19),
(14598,73.19),
(14598,73.19),
(14598, 215.6),
(14598,73.19)
WITH ghosts AS
(
SELECT SysInvNum, REF1, LineBalance,
LAG(LineBalance, 1, 0) OVER (PARTITION BY REF1 ORDER BY SysInvNum) PreviousLineBalance,
LEAD(LineBalance, 1, 0) OVER (PARTITION BY REF1 ORDER BY SysInvNum) NextLineBalance
FROM Receipts
)
SELECT r.SysInvNum, r.REF1, r.LineBalance
FROM Receipts r
JOIN ghosts g On (r.SysInvNum = g.SysInvNum)
WHERE NOT (g.LineBalance + g.PreviousLineBalance = 0 AND g.LineBalance < 0)
AND NOT (g.LineBalance + g.NextLineBalance = 0 AND g.LineBalance > 0)
ORDER BY r.SysInvNum
我会添加一个字段,其中包含明确的标志,告诉您某笔费用是 mistake/reversal 的错误,然后过滤掉这些行就很简单了。即时执行此操作可能会使您的报告相当缓慢。
但是,要按原样解决给定的问题,我们可以这样做。该解决方案假定 SysInvNum
是唯一的。
使用示例数据创建 table
DECLARE @T TABLE (SysInvNum int, REF1 int, LineBalance money);
INSERT INTO @T (SysInvNum, REF1, LineBalance) VALUES (3344299, 14602, 558.83);
INSERT INTO @T (SysInvNum, REF1, LineBalance) VALUES (3344298, 14598, 281.47);
INSERT INTO @T (SysInvNum, REF1, LineBalance) VALUES (3344297, 14602, -95.98);
INSERT INTO @T (SysInvNum, REF1, LineBalance) VALUES (3344296, 14598, -73.19);
INSERT INTO @T (SysInvNum, REF1, LineBalance) VALUES (3341758, 14598, 73.19);
INSERT INTO @T (SysInvNum, REF1, LineBalance) VALUES (11, 100, 50.00);
INSERT INTO @T (SysInvNum, REF1, LineBalance) VALUES (12, 100, -50.00);
INSERT INTO @T (SysInvNum, REF1, LineBalance) VALUES (13, 100, 50.00);
INSERT INTO @T (SysInvNum, REF1, LineBalance) VALUES (21, 200, -50.00);
INSERT INTO @T (SysInvNum, REF1, LineBalance) VALUES (22, 200, -50.00);
INSERT INTO @T (SysInvNum, REF1, LineBalance) VALUES (23, 200, 50.00);
INSERT INTO @T (SysInvNum, REF1, LineBalance) VALUES (31, 300, -50.00);
INSERT INTO @T (SysInvNum, REF1, LineBalance) VALUES (32, 300, 50.00);
INSERT INTO @T (SysInvNum, REF1, LineBalance) VALUES (33, 300, -50.00);
INSERT INTO @T (SysInvNum, REF1, LineBalance) VALUES (34, 300, 50.00);
INSERT INTO @T (SysInvNum, REF1, LineBalance) VALUES (41, 400, 50.00);
INSERT INTO @T (SysInvNum, REF1, LineBalance) VALUES (42, 400, -50.00);
INSERT INTO @T (SysInvNum, REF1, LineBalance) VALUES (43, 400, 50.00);
INSERT INTO @T (SysInvNum, REF1, LineBalance) VALUES (44, 400, -50.00);
INSERT INTO @T (SysInvNum, REF1, LineBalance) VALUES (45, 400, 50.00);
我添加了几个有多个错误的案例。
编号并计算行数
SELECT
SysInvNum
, REF1
, LineBalance
, ROW_NUMBER() OVER(PARTITION BY REF1, LineBalance ORDER BY SysInvNum) AS rn
, COUNT(*) OVER(PARTITION BY REF1, ABS(LineBalance)) AS cc1
FROM @T AS TT
这是结果集:
SysInvNum REF1 LineBalance rn cc1
11 100 50.00 1 3
12 100 -50.00 1 3
13 100 50.00 2 3
21 200 -50.00 1 3
23 200 50.00 1 3
22 200 -50.00 2 3
31 300 -50.00 1 4
32 300 50.00 1 4
33 300 -50.00 2 4
34 300 50.00 2 4
41 400 50.00 1 5
42 400 -50.00 1 5
43 400 50.00 2 5
44 400 -50.00 2 5
45 400 50.00 3 5
3341758 14598 73.19 1 2
3344296 14598 -73.19 1 2
3344298 14598 281.47 1 1
3344297 14602 -95.98 1 1
3344299 14602 558.83 1 1
您可以看到那些有错误的行的计数 > 1。而且,成对的错误具有相同的行号。因此,我们需要 remove/hide 计数 > 1 的行和具有两个相同行号的行。
确定要删除的行
WITH
CTE_rn
AS
(
SELECT
SysInvNum
, REF1
, LineBalance
, ROW_NUMBER() OVER(PARTITION BY REF1, LineBalance ORDER BY SysInvNum) AS rn
, COUNT(*) OVER(PARTITION BY REF1, ABS(LineBalance)) AS cc1
FROM @T AS TT
)
, CTE_ToRemove
AS
(
SELECT
SysInvNum
, REF1
, LineBalance
, COUNT(*) OVER(PARTITION BY REF1, rn) AS cc2
FROM CTE_rn
WHERE CTE_rn.cc1 > 1
)
SELECT *
FROM CTE_ToRemove
WHERE CTE_ToRemove.cc2 = 2
这是另一个中间结果:
SysInvNum REF1 LineBalance cc2
12 100 -50.00 2
11 100 50.00 2
21 200 -50.00 2
23 200 50.00 2
32 300 50.00 2
31 300 -50.00 2
33 300 -50.00 2
34 300 50.00 2
42 400 -50.00 2
41 400 50.00 2
43 400 50.00 2
44 400 -50.00 2
3344296 14598 -73.19 2
3341758 14598 73.19 2
现在,我们将所有这些放在一起。
最终查询
WITH
CTE_rn
AS
(
SELECT
SysInvNum
, REF1
, LineBalance
, ROW_NUMBER() OVER(PARTITION BY REF1, LineBalance ORDER BY SysInvNum) AS rn
, COUNT(*) OVER(PARTITION BY REF1, ABS(LineBalance)) AS cc1
FROM @T AS TT
)
, CTE_ToRemove
AS
(
SELECT
SysInvNum
, REF1
, LineBalance
, COUNT(*) OVER(PARTITION BY REF1, rn) AS cc2
FROM CTE_rn
WHERE CTE_rn.cc1 > 1
)
SELECT *
FROM @T AS TT
WHERE
TT.SysInvNum NOT IN
(
SELECT CTE_ToRemove.SysInvNum
FROM CTE_ToRemove
WHERE CTE_ToRemove.cc2 = 2
)
ORDER BY SysInvNum;
结果:
SysInvNum REF1 LineBalance
13 100 50.00
22 200 -50.00
45 400 50.00
3344297 14602 -95.98
3344298 14598 281.47
3344299 14602 558.83
请注意,最终结果没有任何 REF = 300 的行,因为有两个更正的错误,它们完全平衡了彼此。
大多数 AR/billing 系统将 "credit memos"(负数)视为现金,在这种情况下,-73.19 将应用于 73.19 LineBalance
,就像客户一样支付了该金额,导致余额为 0 美元。
选项 1:
你们在这个系统中处理现金收据和申请吗?如果是这样,您可以从这些现金申请表中提取数据以显示 SysInvNum 3344296 和 3341758 之间的关系。
选项 2:
我假设 PayAdjust
列用于在客户付款后减少余额,而 LineBalance 是一个计算列,即 Charges + PayAdjust
.
大多数情况下,发生这种情况时,应收账款部门将负责将贷记凭证应用到未清发票,因此 PayAdjust
列在两行之间的净额为 0 美元,这会导致LineBalance
在 2 行的每一行上也为 $0。这可能只是正在使用的系统的培训问题。
这会导致有问题的 3 行看起来像这样,所以你没有问题,你只需在查询中添加 where LineBalance <> 0
来排除这些行,因为 AR 部门(应用信用开始,所以知道这个问题的答案)明确说明信用适用于哪个LineBalance
:
选项 2 首选数据结构:
SysInvNum REF1 Charges PayAdjust LineBalance
----------- ----------- --------------------- --------------------- ---------------------
3344298 14598 281.47 0.00 281.47
3344296 14598 -73.19 73.19 0.00
3341758 14598 73.19 -73.19 0.00
选项 3:
如果没有来自选项 1 或 2 的数据,您会做出许多假设,并且 运行 存在无意中隐藏错误行的风险。
也就是说,这是一个尝试执行您所要求的查询,但我强烈建议您与 AR 部门联系,看看他们是否可以更新这些记录 "PayAdjust"。
我添加了几个可能导致问题的场景测试用例,但这可能无法涵盖所有基础。
对于相同的 REF1 和相同的 DueDate,此查询将仅隐藏找到一个不同的匹配负值作为正值的行。它还确保原始收费发票 ID 在贷记之前,因为可以假设贷记不会在实际收费之前发生(测试用例 6 仍然显示两行,因为贷记具有在收费之前发生的 SysInvNum ).如果每个 REF1, DueDate, and LineBalance
找到多次匹配,则它不会隐藏相应的费用和信用额度(测试用例 2 和 4)。测试用例 3 的总和为 0,但它仍然显示所有 3 行,因为 LineBalance 值不完全匹配。这些都是我为处理边缘情况所做的假设,因此可以根据需要进行调整。
CREATE TABLE #SysInvTable (SysInvNum int not null primary key, REF1 int, Charges money, PayAdjust money, LineBalance as Charges + PayAdjust, DueDate date, REF2 int, Remark varchar(50), REM varchar(50));
INSERT INTO #SysInvTable(SysInvNum, REF1, Charges, PayAdjust, DueDate, Remark)
VALUES
--.....................................
--Your test case
(3344298, 14598, 281.47, 0, '2014-12-08','Your original test case. This one should stay.')
, (3344296, 14598, -73.19, 0, '2014-12-08',null)
, (3341758, 14598, 73.19, 0, '2014-12-08',null)
--.....................................
--Test case 2: How do you match these up?
, (2001, 2, 73.19, 0, '2015-01-06','Charge 2.1')
, (2002, 2, 73.19, 0, '2015-01-06','Charge 2.2')
, (2003, 2, 73.19, 0, '2015-01-06','Charge 2.3')
, (2004, 2, -73.19, 0, '2015-01-06','Credit for charge 2.3')
, (2005, 2, -73.19, 0, '2015-01-06','Credit for charge 2.1')
--.....................................
--Test case 3
, (3001, 3, 73.19, 0, '2015-01-06','Charge 3.1')
, (3002, 3, 73.19, 0, '2015-01-06','Charge 3.2')
, (3003, 3, -146.38, 0, '2015-01-06','Credit for charges 3.1 and 3.2')
--.....................................
--Test case 4: Do you hide 4001 or 4002?
, (4001, 4, 73.19, 0, '2015-01-06','Cable')
, (4002, 4, 73.19, 0, '2015-01-06','Internet')
, (4003, 4, -73.19, 0, '2015-01-06','Misc Credit')
--.....................................
--Test case 5: remove all lines except the first
, (5000, 5, 9.99, 0, '2015-01-06','Charge 5.0 (Should stay)')
, (5001, 5, 11.11, 0, '2015-01-06','Charge 5.1')
, (5002, 5, 22.22, 0, '2015-01-06','Charge 5.2')
, (5003, 5, 33.33, 0, '2015-01-06','Charge 5.3')
, (5004, 5, -11.11, 0, '2015-01-06','Credit for charge 5.1')
, (5005, 5, -33.33, 0, '2015-01-06','Credit for charge 5.3')
, (5006, 5, -22.22, 0, '2015-01-06','Credit for charge 5.2')
--.....................................
--Test case 6: credit occurs before charge, so keep both
, (6000, 6, -73.19, 0, '2015-01-06','Credit occurs before charge')
, (6001, 6, 73.19, 0, '2015-01-06','Charge 6.1')
;
SELECT i.*
FROM #SysInvTable i
WHERE i.SysInvNum not in
(
SELECT IngoreInvNum = case when c.N = 1 then max(t.SysInvNum) else min(t2.SysInvNum) end
FROM #SysInvTable t
INNER JOIN #SysInvTable t2
ON t.ref1 = t2.ref1
AND t.DueDate = t2.DueDate
CROSS APPLY (SELECT 1 AS N UNION ALL SELECT 2 as N) AS c --used to both both T and T2 SysInvNum's to exclude
WHERE 1=1
AND t.LineBalance > 0 AND t2.LineBalance < 0
AND t.SysInvNum < t2.SysInvNum --make sure the credit came in after the positive SysInvNum
AND t.LineBalance = t2.LineBalance * -1
GROUP BY t.REF1, t.DueDate, abs(t.LineBalance), c.n
HAVING Count(*) = 1
)
;
DROP TABLE #SysInvTable;
我有一个查询会导致在我们的 SSRS 2008 R2 服务器上创建客户账单。 SQL 服务器实例也是 2008 R2。查询很大,出于安全等原因,我不想 post 整个查询。
我需要对下面的示例数据做的是从结果集中删除 73.19 和 -73.19 的两行。因此,如果两行在 LineBalance 列中具有相同的绝对值并且它们的总和为 0 并且如果它们在 REF1 列中具有相同的值,则应从结果集中删除。结果集中仍应返回 REF1 = 14598 和行余额为 281.47 的行,不应返回下面 REF1 = 14598 的其他两行。
这里的重点是 "hide" 会计错误和客户的更正。 "hide" 我的意思是,不要在他们收到的邮件账单上显示。这里发生的事情是客户在本应在 281.47 开具账单时错误地在 73.19 开具账单。所以,我们的 AR 部门。将 73.19 退回他们的帐户并向他们收取正确金额 281.47。如您所见,它们都具有相同的 REF1 值。
它很笨重,但它可以让您识别匹配的负面违规者。您需要从结果集中排除两列中的 sysInvNum
Create table #tmp( SysInvNum int,Ref1 int, LineBalance decimal(8,2))
insert into #tmp
values(3344299,14602,558.83)
,(3344298,14598,281.47)
,(3344297,14602,-95.98)
,(3344296,14598,-73.19)
,(3341758,14598,73.19)
--Select * From #tmp
Select *
INTO #t1
From #tmp
Where LineBalance < 0
--Select * From #t1
Select SysInvNum1 = #tmp.SysInvNum, SysInvNum2 = #T1.SysInvNum
INTO #T2
From #tmp
LEFT JOIN #t1 On #tmp.Ref1 = #T1.Ref1 and #tmp.LineBalance = -#T1.LineBalance
where #t1.SysInvNum is not null
Select * From
#tmp
Where SysInvNum not in(
Select SysInvNum1 from #t2
union Select SysInvNum2 from #t2
)
drop table #tmp
drop table #t1
drop table #t2
此解决方案仅适用于 SQL Server 2012,但如果将来有人必须做类似的事情,我决定将其保留在这里,因为它非常简单.
.....
如果总和为 0 并且它们之间没有其他交易(负交易发生在正交易之后),这应该只删除特定收件人的两笔交易。比较保守和安全。
应该有关于交易顺序的信息。类似日期列的东西。在 PARTITION BY 语句中,您应该按此列而不是 PRIMARY KEY 列进行排序。
IF OBJECT_ID('Receipts', 'U') IS NOT NULL
DROP TABLE Receipts
CREATE TABLE Receipts
(
SysInvNum INT PRIMARY KEY IDENTITY(1,1),
REF1 INT,
LineBalance DECIMAL(10,2)
)
INSERT INTO Receipts
values
(14602,558.83),
(14598,281.47),
(14602,-95.98),
(14598,73.19),
(14598,-73.19),
(14598,73.19),
(14598,73.19),
(14598, 215.6),
(14598,73.19)
WITH ghosts AS
(
SELECT SysInvNum, REF1, LineBalance,
LAG(LineBalance, 1, 0) OVER (PARTITION BY REF1 ORDER BY SysInvNum) PreviousLineBalance,
LEAD(LineBalance, 1, 0) OVER (PARTITION BY REF1 ORDER BY SysInvNum) NextLineBalance
FROM Receipts
)
SELECT r.SysInvNum, r.REF1, r.LineBalance
FROM Receipts r
JOIN ghosts g On (r.SysInvNum = g.SysInvNum)
WHERE NOT (g.LineBalance + g.PreviousLineBalance = 0 AND g.LineBalance < 0)
AND NOT (g.LineBalance + g.NextLineBalance = 0 AND g.LineBalance > 0)
ORDER BY r.SysInvNum
我会添加一个字段,其中包含明确的标志,告诉您某笔费用是 mistake/reversal 的错误,然后过滤掉这些行就很简单了。即时执行此操作可能会使您的报告相当缓慢。
但是,要按原样解决给定的问题,我们可以这样做。该解决方案假定 SysInvNum
是唯一的。
使用示例数据创建 table
DECLARE @T TABLE (SysInvNum int, REF1 int, LineBalance money);
INSERT INTO @T (SysInvNum, REF1, LineBalance) VALUES (3344299, 14602, 558.83);
INSERT INTO @T (SysInvNum, REF1, LineBalance) VALUES (3344298, 14598, 281.47);
INSERT INTO @T (SysInvNum, REF1, LineBalance) VALUES (3344297, 14602, -95.98);
INSERT INTO @T (SysInvNum, REF1, LineBalance) VALUES (3344296, 14598, -73.19);
INSERT INTO @T (SysInvNum, REF1, LineBalance) VALUES (3341758, 14598, 73.19);
INSERT INTO @T (SysInvNum, REF1, LineBalance) VALUES (11, 100, 50.00);
INSERT INTO @T (SysInvNum, REF1, LineBalance) VALUES (12, 100, -50.00);
INSERT INTO @T (SysInvNum, REF1, LineBalance) VALUES (13, 100, 50.00);
INSERT INTO @T (SysInvNum, REF1, LineBalance) VALUES (21, 200, -50.00);
INSERT INTO @T (SysInvNum, REF1, LineBalance) VALUES (22, 200, -50.00);
INSERT INTO @T (SysInvNum, REF1, LineBalance) VALUES (23, 200, 50.00);
INSERT INTO @T (SysInvNum, REF1, LineBalance) VALUES (31, 300, -50.00);
INSERT INTO @T (SysInvNum, REF1, LineBalance) VALUES (32, 300, 50.00);
INSERT INTO @T (SysInvNum, REF1, LineBalance) VALUES (33, 300, -50.00);
INSERT INTO @T (SysInvNum, REF1, LineBalance) VALUES (34, 300, 50.00);
INSERT INTO @T (SysInvNum, REF1, LineBalance) VALUES (41, 400, 50.00);
INSERT INTO @T (SysInvNum, REF1, LineBalance) VALUES (42, 400, -50.00);
INSERT INTO @T (SysInvNum, REF1, LineBalance) VALUES (43, 400, 50.00);
INSERT INTO @T (SysInvNum, REF1, LineBalance) VALUES (44, 400, -50.00);
INSERT INTO @T (SysInvNum, REF1, LineBalance) VALUES (45, 400, 50.00);
我添加了几个有多个错误的案例。
编号并计算行数
SELECT
SysInvNum
, REF1
, LineBalance
, ROW_NUMBER() OVER(PARTITION BY REF1, LineBalance ORDER BY SysInvNum) AS rn
, COUNT(*) OVER(PARTITION BY REF1, ABS(LineBalance)) AS cc1
FROM @T AS TT
这是结果集:
SysInvNum REF1 LineBalance rn cc1
11 100 50.00 1 3
12 100 -50.00 1 3
13 100 50.00 2 3
21 200 -50.00 1 3
23 200 50.00 1 3
22 200 -50.00 2 3
31 300 -50.00 1 4
32 300 50.00 1 4
33 300 -50.00 2 4
34 300 50.00 2 4
41 400 50.00 1 5
42 400 -50.00 1 5
43 400 50.00 2 5
44 400 -50.00 2 5
45 400 50.00 3 5
3341758 14598 73.19 1 2
3344296 14598 -73.19 1 2
3344298 14598 281.47 1 1
3344297 14602 -95.98 1 1
3344299 14602 558.83 1 1
您可以看到那些有错误的行的计数 > 1。而且,成对的错误具有相同的行号。因此,我们需要 remove/hide 计数 > 1 的行和具有两个相同行号的行。
确定要删除的行
WITH
CTE_rn
AS
(
SELECT
SysInvNum
, REF1
, LineBalance
, ROW_NUMBER() OVER(PARTITION BY REF1, LineBalance ORDER BY SysInvNum) AS rn
, COUNT(*) OVER(PARTITION BY REF1, ABS(LineBalance)) AS cc1
FROM @T AS TT
)
, CTE_ToRemove
AS
(
SELECT
SysInvNum
, REF1
, LineBalance
, COUNT(*) OVER(PARTITION BY REF1, rn) AS cc2
FROM CTE_rn
WHERE CTE_rn.cc1 > 1
)
SELECT *
FROM CTE_ToRemove
WHERE CTE_ToRemove.cc2 = 2
这是另一个中间结果:
SysInvNum REF1 LineBalance cc2
12 100 -50.00 2
11 100 50.00 2
21 200 -50.00 2
23 200 50.00 2
32 300 50.00 2
31 300 -50.00 2
33 300 -50.00 2
34 300 50.00 2
42 400 -50.00 2
41 400 50.00 2
43 400 50.00 2
44 400 -50.00 2
3344296 14598 -73.19 2
3341758 14598 73.19 2
现在,我们将所有这些放在一起。
最终查询
WITH
CTE_rn
AS
(
SELECT
SysInvNum
, REF1
, LineBalance
, ROW_NUMBER() OVER(PARTITION BY REF1, LineBalance ORDER BY SysInvNum) AS rn
, COUNT(*) OVER(PARTITION BY REF1, ABS(LineBalance)) AS cc1
FROM @T AS TT
)
, CTE_ToRemove
AS
(
SELECT
SysInvNum
, REF1
, LineBalance
, COUNT(*) OVER(PARTITION BY REF1, rn) AS cc2
FROM CTE_rn
WHERE CTE_rn.cc1 > 1
)
SELECT *
FROM @T AS TT
WHERE
TT.SysInvNum NOT IN
(
SELECT CTE_ToRemove.SysInvNum
FROM CTE_ToRemove
WHERE CTE_ToRemove.cc2 = 2
)
ORDER BY SysInvNum;
结果:
SysInvNum REF1 LineBalance
13 100 50.00
22 200 -50.00
45 400 50.00
3344297 14602 -95.98
3344298 14598 281.47
3344299 14602 558.83
请注意,最终结果没有任何 REF = 300 的行,因为有两个更正的错误,它们完全平衡了彼此。
大多数 AR/billing 系统将 "credit memos"(负数)视为现金,在这种情况下,-73.19 将应用于 73.19 LineBalance
,就像客户一样支付了该金额,导致余额为 0 美元。
选项 1:
你们在这个系统中处理现金收据和申请吗?如果是这样,您可以从这些现金申请表中提取数据以显示 SysInvNum 3344296 和 3341758 之间的关系。
选项 2:
我假设 PayAdjust
列用于在客户付款后减少余额,而 LineBalance 是一个计算列,即 Charges + PayAdjust
.
大多数情况下,发生这种情况时,应收账款部门将负责将贷记凭证应用到未清发票,因此 PayAdjust
列在两行之间的净额为 0 美元,这会导致LineBalance
在 2 行的每一行上也为 $0。这可能只是正在使用的系统的培训问题。
这会导致有问题的 3 行看起来像这样,所以你没有问题,你只需在查询中添加 where LineBalance <> 0
来排除这些行,因为 AR 部门(应用信用开始,所以知道这个问题的答案)明确说明信用适用于哪个LineBalance
:
选项 2 首选数据结构:
SysInvNum REF1 Charges PayAdjust LineBalance
----------- ----------- --------------------- --------------------- ---------------------
3344298 14598 281.47 0.00 281.47
3344296 14598 -73.19 73.19 0.00
3341758 14598 73.19 -73.19 0.00
选项 3:
如果没有来自选项 1 或 2 的数据,您会做出许多假设,并且 运行 存在无意中隐藏错误行的风险。
也就是说,这是一个尝试执行您所要求的查询,但我强烈建议您与 AR 部门联系,看看他们是否可以更新这些记录 "PayAdjust"。
我添加了几个可能导致问题的场景测试用例,但这可能无法涵盖所有基础。
对于相同的 REF1 和相同的 DueDate,此查询将仅隐藏找到一个不同的匹配负值作为正值的行。它还确保原始收费发票 ID 在贷记之前,因为可以假设贷记不会在实际收费之前发生(测试用例 6 仍然显示两行,因为贷记具有在收费之前发生的 SysInvNum ).如果每个 REF1, DueDate, and LineBalance
找到多次匹配,则它不会隐藏相应的费用和信用额度(测试用例 2 和 4)。测试用例 3 的总和为 0,但它仍然显示所有 3 行,因为 LineBalance 值不完全匹配。这些都是我为处理边缘情况所做的假设,因此可以根据需要进行调整。
CREATE TABLE #SysInvTable (SysInvNum int not null primary key, REF1 int, Charges money, PayAdjust money, LineBalance as Charges + PayAdjust, DueDate date, REF2 int, Remark varchar(50), REM varchar(50));
INSERT INTO #SysInvTable(SysInvNum, REF1, Charges, PayAdjust, DueDate, Remark)
VALUES
--.....................................
--Your test case
(3344298, 14598, 281.47, 0, '2014-12-08','Your original test case. This one should stay.')
, (3344296, 14598, -73.19, 0, '2014-12-08',null)
, (3341758, 14598, 73.19, 0, '2014-12-08',null)
--.....................................
--Test case 2: How do you match these up?
, (2001, 2, 73.19, 0, '2015-01-06','Charge 2.1')
, (2002, 2, 73.19, 0, '2015-01-06','Charge 2.2')
, (2003, 2, 73.19, 0, '2015-01-06','Charge 2.3')
, (2004, 2, -73.19, 0, '2015-01-06','Credit for charge 2.3')
, (2005, 2, -73.19, 0, '2015-01-06','Credit for charge 2.1')
--.....................................
--Test case 3
, (3001, 3, 73.19, 0, '2015-01-06','Charge 3.1')
, (3002, 3, 73.19, 0, '2015-01-06','Charge 3.2')
, (3003, 3, -146.38, 0, '2015-01-06','Credit for charges 3.1 and 3.2')
--.....................................
--Test case 4: Do you hide 4001 or 4002?
, (4001, 4, 73.19, 0, '2015-01-06','Cable')
, (4002, 4, 73.19, 0, '2015-01-06','Internet')
, (4003, 4, -73.19, 0, '2015-01-06','Misc Credit')
--.....................................
--Test case 5: remove all lines except the first
, (5000, 5, 9.99, 0, '2015-01-06','Charge 5.0 (Should stay)')
, (5001, 5, 11.11, 0, '2015-01-06','Charge 5.1')
, (5002, 5, 22.22, 0, '2015-01-06','Charge 5.2')
, (5003, 5, 33.33, 0, '2015-01-06','Charge 5.3')
, (5004, 5, -11.11, 0, '2015-01-06','Credit for charge 5.1')
, (5005, 5, -33.33, 0, '2015-01-06','Credit for charge 5.3')
, (5006, 5, -22.22, 0, '2015-01-06','Credit for charge 5.2')
--.....................................
--Test case 6: credit occurs before charge, so keep both
, (6000, 6, -73.19, 0, '2015-01-06','Credit occurs before charge')
, (6001, 6, 73.19, 0, '2015-01-06','Charge 6.1')
;
SELECT i.*
FROM #SysInvTable i
WHERE i.SysInvNum not in
(
SELECT IngoreInvNum = case when c.N = 1 then max(t.SysInvNum) else min(t2.SysInvNum) end
FROM #SysInvTable t
INNER JOIN #SysInvTable t2
ON t.ref1 = t2.ref1
AND t.DueDate = t2.DueDate
CROSS APPLY (SELECT 1 AS N UNION ALL SELECT 2 as N) AS c --used to both both T and T2 SysInvNum's to exclude
WHERE 1=1
AND t.LineBalance > 0 AND t2.LineBalance < 0
AND t.SysInvNum < t2.SysInvNum --make sure the credit came in after the positive SysInvNum
AND t.LineBalance = t2.LineBalance * -1
GROUP BY t.REF1, t.DueDate, abs(t.LineBalance), c.n
HAVING Count(*) = 1
)
;
DROP TABLE #SysInvTable;