删除为给定密钥求零的行

Bre*_*orn 9 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值.

我的数据的一个例子

Vla*_*nov 6

我会添加一个字段,其中包含显式标记,告诉您某个指控是错误/错误的反转,然后过滤掉这些行是微不足道的.即时执行可能会使您的报告变得相当缓慢.

但是,为了解决给定的问题,我们可以这样做.该解决方案假定这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);
Run Code Online (Sandbox Code Playgroud)

我添加了几个有多个错误的案例.

数字和计数行

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
Run Code Online (Sandbox Code Playgroud)

这是结果集:

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
Run Code Online (Sandbox Code Playgroud)

您可以看到那些有错误的行计数> 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
Run Code Online (Sandbox Code Playgroud)

这是另一个中间结果:

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
Run Code Online (Sandbox Code Playgroud)

现在,我们把所有这些放在一起.

最终查询

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;
Run Code Online (Sandbox Code Playgroud)

结果:

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
Run Code Online (Sandbox Code Playgroud)

注意,最终结果没有REF = 300的任何行,因为有两个纠正的错误,它们完全相互平衡.


Bat*_*ech 3

大多数 AR/计费系统将“贷项凭证”(负金额)视为与现金类似,在这种情况下,-73.19 将应用于 73.19,LineBalance就像客户已支付该金额一样,从而导致余额为 0 美元。

选项1:

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

选项 2:

我假设该PayAdjust列用于减少客户付款后的余额,并且 LineBalance 是一个计算列,即Charges + PayAdjust

大多数情况下,发生这种情况时,AR 部门将负责将贷项凭证应用于未结发票,以便该PayAdjust列在 2 行之间净值为 0 美元,这将导致LineBalance2 行中的每行也为 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
Run Code Online (Sandbox Code Playgroud)

选项 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;
Run Code Online (Sandbox Code Playgroud)