删除一些行以使每个组的总数低于阈值

Delete some rows to bring each group total below the threshold

我有一个 Ledger table:

CREATE TABLE Ledger
 (
     PersonID int,
     Narration varchar(255),
     Payment int(255)
 ); 

INSERT INTO Ledger(PersonID, Narration, Payment)
 VALUES (1, 'Snacks 1', 5); 
INSERT INTO Ledger(PersonID, Narration, Payment)
 VALUES (1, 'Snacks 2', 10); 
INSERT INTO Ledger(PersonID, Narration, Payment)
 VALUES (2, 'Snacks 3', 7); 
INSERT INTO Ledger(PersonID, Narration, Payment)
 VALUES (1, 'Snacks 4', 6); 
INSERT INTO Ledger(PersonID, Narration, Payment)
 VALUES (2, 'Snacks 5', 3); 
INSERT INTO Ledger(PersonID, Narration, Payment)
 VALUES (1, 'Snacks 6', 1); 

table 看起来像这样:

PersonID          Narration          Payment
_____________________________________________
    1             Snacks 1                5
    1             Snacks 2               10
    2             Snacks 3                7
    1             Snacks 4                6
    2             Snacks 5                3
    1             Snacks 6                1

这里PersonID=1一共花了22,PersonID=2一共花了10。

我的要求是将总数 Payment 减少到小于或等于 20。没有唯一的列我希望删除记录以使总数 Payment 低于或等于 20

在上面table中,PersonID=1一共Payment大于20,所以我需要删除一些记录来减少总支付。

我的预期输出

PersonID          Narration          Payment
_____________________________________________

    1             Snacks 2               10
    2             Snacks 3                7
    1             Snacks 4                6
    2             Snacks 5                3
    1             Snacks 6                1

这里我去掉了

1             Snacks 1                5

现在PersonID=1的总Payment是17,低于20

根据逻辑我们必须删除记录。

请在 SQL 服务器和 MySQL 方面协助我。我的第一选择是 SQL 服务器。

这是 SQL Server 2012+ 的一种可能变体。

示例数据

CREATE TABLE Ledger
(
    PersonID int,
    Narration varchar(255),
    Payment int
);

INSERT INTO Ledger(PersonID, Narration, Payment) VALUES 
(1, 'Snacks 1', 5),
(1, 'Snacks 2', 10),
(2, 'Snacks 3', 7),
(1, 'Snacks 4', 6),
(2, 'Snacks 5', 3),
(1, 'Snacks 6', 1);

SELECT *
FROM Ledger
ORDER BY PersonID, Payment;

+----------+-----------+---------+
| PersonID | Narration | Payment |
+----------+-----------+---------+
|        1 | Snacks 6  |       1 |
|        1 | Snacks 1  |       5 |
|        1 | Snacks 4  |       6 |
|        1 | Snacks 2  |      10 |
|        2 | Snacks 5  |       3 |
|        2 | Snacks 3  |       7 |
+----------+-----------+---------+

查询

我们可以计算 运行 总数以确定我们要保留哪些行以及要删除哪些行。您可以通过选择 运行 总数中的排序来调整行选择的逻辑。在此示例中,我将从最小的 Payment 开始计算 运行 总数,因此将保留最小 Payment 的行。

此查询显示计算,以了解其工作原理:

WITH
CTE
AS
(
    SELECT
        PersonID
        ,Narration
        ,Payment
        ,SUM(Payment) OVER 
            (PARTITION BY PersonID ORDER BY Payment
            ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW) AS ss
    FROM Ledger
)
SELECT *
FROM CTE
ORDER BY PersonID, Payment;


+----------+-----------+---------+----+
| PersonID | Narration | Payment | ss |
+----------+-----------+---------+----+
|        1 | Snacks 6  |       1 |  1 |
|        1 | Snacks 1  |       5 |  6 |
|        1 | Snacks 4  |       6 | 12 |
|        1 | Snacks 2  |      10 | 22 |
|        2 | Snacks 5  |       3 |  3 |
|        2 | Snacks 3  |       7 | 10 |
+----------+-----------+---------+----+

这个查询实际上删除了行:

WITH
CTE
AS
(
    SELECT
        PersonID
        ,Narration
        ,Payment
        ,SUM(Payment) OVER 
            (PARTITION BY PersonID ORDER BY Payment
            ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW) AS ss
    FROM Ledger
)
DELETE FROM CTE
WHERE ss > 20;

结果

SELECT *
FROM Ledger
ORDER BY PersonID, Payment;

+----------+-----------+---------+
| PersonID | Narration | Payment |
+----------+-----------+---------+
|        1 | Snacks 6  |       1 |
|        1 | Snacks 1  |       5 |
|        1 | Snacks 4  |       6 |
|        2 | Snacks 5  |       3 |
|        2 | Snacks 3  |       7 |
+----------+-----------+---------+

您还可以通过以下查询获得所需的输出:-

DECLARE @PID int ,@PID1 int,@Narr VARCHAR(250),@Payment decimal(18,2),@Payment1 decimal(18,2),@cnt int

SET @cnt = 20
set @Payment1=0
set @PID1=0

Create table #t1(PersonID int,Narration varchar(250),Payment decimal(18,2));

DECLARE db_cursor CURSOR FOR  
SELECT PersonID,Narration,Payment from Ledger order by personid ,Payment

OPEN db_cursor   
FETCH NEXT FROM db_cursor INTO @PID,@Narr,@Payment   

WHILE @@FETCH_STATUS = 0   
BEGIN  
if (@PID1 <> @PID)
BEGIN
SET @Payment1 = 0
END
set @PID1 = @PID
SET @Payment1 = @Payment1 + @Payment

If(@Payment1 <= 20)
begin

Insert into #t1(PersonID,Narration,Payment)
values(@PID,@Narr,@Payment)

end
FETCH NEXT FROM db_cursor INTO @PID,@Narr,@Payment    
end
CLOSE db_cursor   
DEALLOCATE db_cursor

select * from #t1
order by personid
drop table #t1

同样进行了测试,得到了以下输入的以下输出:-

输入:-

1   Snacks 1    5
1   Snacks 2    10
1   Snacks 4    6
1   Snacks 6    1
2   Snacks 5    3
2   Snacks 3    7
3   Snacks 7    15
3   Snacks 8    15
4   Snacks 9    10

输出:-

 1  Snacks 6    1.00
 1  Snacks 1    5.00
 1  Snacks 4    6.00
 2  Snacks 5    3.00
 2  Snacks 3    7.00
 3  Snacks 7    15.00
 4  Snacks 9    10.00