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

时间:2016-05-18 05:11:24

标签: sql sql-server sql-server-2012 sql-delete delete-row

我有一个Ledger表:

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); 

表格如下:

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

在上表中,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

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

基于逻辑,我们必须删除记录。

请在SQL Server和MySQL中帮助我。我的第一个偏好是SQL Server。

2 个答案:

答案 0 :(得分:3)

以下是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 |
+----------+-----------+---------+

答案 1 :(得分:0)

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

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