删除为给定密钥求零的行

时间:2015-01-07 22:40:09

标签: sql sql-server

我的查询将导致在我们的SSRS 2008 R2服务器上创建客户账单。 SQL Server实例也是2008 R2。查询很大,出于安全原因等我不想发布整个内容。

我需要对下面的示例数据执行的操作是从结果集中删除带有73.19和-73.19的两行。因此,如果两行在LineBalance列中具有相同的绝对值且它们的总和为0并且如果它们在REF1列中具有相同的值,则应从结果集中删除它们。 REF1 = 14598和行余额281.47的行仍应在结果集中返回,下面的其他两行不应返回REF1 = 14598。

这一点是“隐藏”会计错误及其对客户的纠正。通过“隐藏”我的意思是,不要在他们收到的邮件中显示它。这里发生的是客户被错误地收费73.19,他们应该收到281.47的账单。所以,我们的AR部门。返回73.19到他们的帐户,并向他们收取正确数额281.47。如您所见,它们都具有相同的REF1值。

An Example Of My Data

4 个答案:

答案 0 :(得分:5)

我会添加一个包含显式标记的字段,告诉您某个指控是错误/错误的反转然后过滤掉这些行是微不足道的。即时执行此操作可能会使您的报告速度变慢。

但是,要解决给定的问题,我们可以这样做。该解决方案假定SysInvNum是唯一的。

创建包含样本数据的表

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和那些有两个相同行号的那些。

确定要删除的行

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的任何行,因为有两个纠正的错误,它们完全相互平衡。

答案 1 :(得分:3)

大多数AR /结算系统将“贷项通知单”(负数)视为与现金类似,在这种情况下,-73.19将应用于73.19 LineBalance,就像客户已支付该金额一样,结果为0美元。

选项1:

您是否处理此系统中的现金收据和应用程序?如果是这样,您可以从这些现金申请表中提取数据,以显示SysInvNum 3344296和3341758之间的关系。

选项2:

我假设PayAdjust列用于在客户付款后减少余额,而LineBalance是一个Charges + PayAdjust的计算列。

大多数情况下,AR部门负责将贷项凭证应用于未结发票,以便PayAdjust列在2行之间净赚0美元,这将导致{ {1}}在每两行中也是$ 0。它可能只是正在使用的系统的培训问题。

这会导致有问题的3行看起来像这样,所以你没有问题,你只需要通过将LineBalance添加到查询中来排除行,因为AR部门(应用了信用额度)开头,因此知道这个问题的答案)明确说明信用适用于where LineBalance <> 0

选项2首选数据结构:

LineBalance

选项3:

如果没有来自选项1或2的数据,您可以做出许多假设,并冒着无意中隐藏错误行的风险。

话虽如此,这是一个试图做你要求的查询,但我强烈建议检查AR部门,看看他们是否可以为这些记录更新“PayAdjust”。

我添加了几个可能导致问题的情景测试用例,但这可能无法覆盖所有基础。

对于相同的REF1和相同的DueDate,此查询仅隐藏为正值找到一个不同匹配负值的行。它还确保原始收费发票ID在信用之前,因为可以假设在实际收费之前不会发生信用(测试案例6显示两行仍然是因为信用证在收费之前发生了SysInvNum )。如果每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 找到多次匹配,则不会隐藏相应的费用和信用额度(测试用例2和4)。测试用例3总计为0,但它仍显示所有3行,因为LineBalance值不完全匹配。这些都是我处理边缘情况的假设,因此可以根据需要进行调整。

REF1, DueDate, and LineBalance

答案 2 :(得分:0)

它很笨重,但是这可以让你找出匹配的负面违规者。您需要从结果集中排除两列中的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

答案 3 :(得分:0)

此解决方案适用于仅限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