2016-06-16 1 views
1

У нас есть следующая таблица 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 будет дебетоваться $ 1,2 и остаётся $ 0,8 от id 2 и так далее. Стол теперь будет выглядеть так:

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 

До сих пор мы делали это с использованием грубой силы. Любое предложение идеи, как сделать это более эффективно в минимальном или одиночном запросе.

Обновление: так как кто-то спросил о нашем текущем подходе с грубой силой, он выполняет итерацию каждой строки от самого старого и обновления до тех пор, пока сумма покупки не будет покрыта, что очень неэффективно.

Thanks

+0

Аналогичные вопросы были заданы. – Strawberry

+0

@Strawberry: Отправьте ссылку пожалуйста –

+0

Что вы подразумеваете под «подбором грубой силы»? – Shadow

ответ

3

Используя переменные, вы вычисляете итоговые кредиты. Запустите внутренний запрос, чтобы вы узнали, что сначала подсчитывает 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) 
; 
+0

Спасибо, Хуан, это замечательно. – yumoji

0

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,[email protected])) 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'; 
Смежные вопросы