【问题标题】:Update oldest rows based on sum of column根据列的总和更新最旧的行
【发布时间】:2016-06-16 14:28:20
【问题描述】:

我们有一个以下 MySQL 表来维护用户信用。

id   user    credits      expiry        status
-----------------------------------------
1     A       1.2        somedatetime    0
2     A       4.4        somedatetime    0  
3     A       5.0        somedatetime    0  
4     B       1.0        somedatetime    0  
5     B       2.4        somedatetime    0  
6     C       7.8        somedatetime    0  

每当用户进行购买时,我们都会从可用积分中扣除金额。为了对用户公平起见,最近到期的积分将首先被消耗,依此类推。我们还将更新状态以将行标记为已使用。

例如,如果用户 A 购买了 2 美元,则 1.2 美元将从 id 1 中扣除,剩余 0.8 美元将从 id 2 中扣除,依此类推。所以 Table 现在看起来像

id   user    credits      expiry        status
-----------------------------------------
1     A       0.0        somedatetime    1
2     A       3.6        somedatetime    1  
3     A       5.0        somedatetime    0  
4     B       1.0        somedatetime    0  
5     B       2.4        somedatetime    0  
6     C       7.8        somedatetime    0  

到目前为止,我们一直在使用蛮力方法。任何想法建议如何在最少或单个查询中更有效地做到这一点。

更新:因为有人问起我们目前的蛮力方法,它从最旧的开始迭代每一行并更新直到覆盖购买金额,这是非常低效的。

谢谢

【问题讨论】:

  • 已提出类似问题。
  • @Strawberry:请发布链接
  • “蛮力方法”是什么意思?
  • @shadow 逐行读取并更新直到覆盖购买金额。
  • 2 美元中,1.2 美元将从 id 1 中扣除,剩余 0.6 美元 我的数学不太好,但我还有 0.8跨度>

标签: mysql sql


【解决方案1】:

使用变量计算总学分。运行内部查询,以便您首先了解什么是 beign 计算。

Fiddle Demo

UPDATE customer c
JOIN (
        SELECT cu.`id`, 
               cu.`user`, 
               `credits`, `expiry`, `status`,        
               @total := IF(@customer = cu.`user`, @total := @total  + `credits`, `credits`) as cumulative_total,
               @customer := cu.`user` as user_current,
               `credit_used`
        FROM customer cu 
        CROSS JOIN  (SELECT @customer := '', @total := 0 ) t
        JOIN credits
          ON cu.`user` = credits.`user`
        ORDER BY cu.`id`
     ) t
   ON c.`id` = t.`id`
SET c.credits = CASE WHEN c.credits <= t.credit_used THEN 0
                                                     ELSE t.cumulative_total - credit_used
                END,
    c.status =  CASE WHEN c.credits <= t.credit_used THEN 1
                                                     ELSE 0
                END;

我的测试设置:

CREATE TABLE customer
    (`id` int, `user` varchar(1), `credits` double, `expiry` int, `status` int)
;

INSERT INTO customer
    (`id`, `user`, `credits`, `expiry`, `status`)
VALUES
    (1, 'A', 1.2, 1, 0),
    (2, 'A', 4.4, 2, 0),
    (3, 'A', 5.0, 3, 0),
    (4, 'B', 1.0, 4, 0),
    (5, 'B', 2.4, 5, 0),
    (6, 'C', 7.8, 6, 0)
;

CREATE TABLE credits
    (`id` int, `user` varchar(1), `credit_used` double)
;

INSERT INTO credits
    (`id`, `user`, `credit_used`)
VALUES
    (1, 'A', 2.0),
    (2, 'B', 3.4)
;

【讨论】:

    【解决方案2】:

    http://sqlfiddle.com/#!9/485673/1

    SET @amount = 2;
    UPDATE t1
    JOIN (
      SELECT t2.id, 
        IF(@amount=0,t2.credits, IF(@amount>t2.credits,0,t2.credits-@amount)) credits,
        IF(@amount>=t2.credits,@amount := @amount-t2.credits, 0)
      FROM (
        SELECT id, credits
        FROM t1
        WHERE credits>0 AND `user`='A'
        ORDER BY expiry ASC
      ) t2
               ) t
    ON t1.id = t.id
    SET t1.credits=t.credits
    WHERE t1.user = 'A';
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2021-04-28
      • 1970-01-01
      • 1970-01-01
      • 2022-08-16
      • 1970-01-01
      • 2020-07-21
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多