2017-02-05 6 views
0

У меня есть таблица транзакций, который записывает каждую сумму добавляется или вычтен из баланса Клиента, с новым балансом:Получить последнее изменение баланса знака в (My) SQL

+----+------------+------------+--------+---------+ 
| id | customerId | timestamp | amount | balance | 
+----+------------+------------+--------+---------+ 
| 1 |   1 | 1000000001 |  10 |  10 | 
| 2 |   1 | 1000000002 | -20 |  -10 | 
| 3 |   1 | 1000000003 | -10 |  -20 | 
| 4 |   2 | 1000000004 |  -5 |  -5 | 
| 5 |   2 | 1000000005 |  -5 |  -10 | 
| 6 |   2 | 1000000006 |  10 |  0 | 
| 7 |   3 | 1000000007 |  -5 |  -5 | 
| 8 |   3 | 1000000008 |  10 |  5 | 
| 9 |   3 | 1000000009 |  10 |  15 | 
| 10 |   4 | 1000000010 |  5 |  5 | 
+----+------------+------------+--------+---------+ 

таблица Клиент сохраняет текущий баланс, и выглядит следующим образом:

+----+---------+ 
| id | balance | 
+----+---------+ 
| 1 |  -20 | 
| 2 |  0 | 
| 3 |  15 | 
| 4 |  5 | 
+----+---------+ 

Я хотел бы добавить balanceSignSince столбец, который будет хранить метку времени, при котором знак баланса последнего изменения. Переход от положительный, отрицательный, или ноль рассчитывается как изменение баланса.

После обновления, основываясь на приведенных выше данных, таблица Клиента должна содержать:

+----+---------+------------------+ 
| id | balance | balanceSignSince | 
+----+---------+------------------+ 
| 1 |  -20 |  1000000002 | 
| 2 |  0 |  1000000006 | 
| 3 |  15 |  1000000008 | 
| 4 |  5 |  1000000010 | 
+----+---------+------------------+ 

Как я могу написать запрос SQL, который обновляет каждый клиент с моментом последнего знак баланса изменился, основанный на таблицу транзакций?

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

+0

Почему будет ли клиент 1 иметь значение '1000000002', а не' 1000000003'? –

+0

Потому что это когда знак баланса перешел на отрицательный. Следующая сделка не повлияла на знак баланса. – Benjamin

+0

@McNets Это ..? – Benjamin

ответ

1

Используется имитируемая функция rank().

select customerId, min(tstamp) from 
(
select tstamp, 
     if (@cust = customerId and sign(@bal) = sign(balance), @rn := @rn, 
      if (@cust = customerId and sign(@bal) <> sign(balance), @rn := @rn + 1, @rn := 0)) as rn, 
     @cust := customerId as customerId, @bal := balance as balance 
from 
     (select @rn := 0) x, 
     (select id, @cust := customerId as customerId, tstamp, amount, @bal := balance as balance 
     from trans order by customerId, tstamp desc) y 
) z 
where rn = 0 
group by customerId; 

Проверьте это: http://rextester.com/XJVKK61181

Этот скрипт возвращает таблицу, как это:

+------------+----+------------+---------+ 
| tstamp  | rn | customerId | balance | 
+------------+----+------------+---------+ 
| 1000000003 | 0 | 1   | -20  | 
| 1000000002 | 0 | 1   | -10  | 
| 1000000001 | 1 | 1   | 10  | 
| 1000000006 | 0 | 2   | 0  | 
| 1000000005 | 2 | 2   | -10  | 
| 1000000004 | 2 | 2   | -5  | 
| 1000000009 | 0 | 3   | 15  | 
| 1000000008 | 2 | 3   | 5  | 
| 1000000007 | 3 | 3   | -5  | 
| 1000000010 | 0 | 4   | 5  | 
+------------+----+------------+---------+ 

Затем выбора мин (метки времени) файлов, где гп = 0:

+------------+-------------+ 
| customerId | min(tstamp) | 
+------------+-------------+ 
| 1   | 1000000002 | 
+------------+-------------+ 
| 2   | 1000000006 | 
+------------+-------------+ 
| 3   | 1000000009 | 
+------------+-------------+ 
| 4   | 1000000010 | 
+------------+-------------+ 
+0

Wow, MAGIC. Это прекрасно работает. Спасибо! – Benjamin

1

Обновленный ответ с тем ограничением, что это необходимо для работы над существующими данными

Следующий запрос должен работать в большинстве случаев, до сих пор существует проблема с клиентами, имеющими только одну сделку или нет изменения знака. Поскольку это обновление один раз, я бы выполнить запрос ниже, а затем сделать простое обновление для всех пользователей, не имеющих набор временных меток, для них это будет метка времени первой сделки:

# Find the smallest timestamp, e.g. the 
# transaction which changed the signum. 
SELECT 
    p.customerId as customerId, 
    MIN(t.timestamp) as balanceSignSince 
FROM 
    transaction as t, 
    (
     # find the latest timestamp having 
     # a different sign for each user. 
     # Here is the issue with users having 
     # only a single transaction or no sign 
     # changes. 
     SELECT 
     u.customerId as customerId, 
     MAX(t.timestamp) as balanceSignSince 
     FROM 
     transaction as t, 
     customer as c, 
     (
      # find the timestamp of the very last 
      # transaction for every user. 
      SELECT 
      t.customerId as customerId, 
      MAX(t.timestamp) as lastTransaction    
      FROM 
      transaction as t 
      GROUP BY 
      t.customerId 
     ) as u 
     WHERE 
      u.customerId = c.id 
      AND u.customerId = t.customerId 
      AND SIGN(c.balance) <> SIGN(t.balance) 
     GROUP BY 
     u.customerId 
    ) as p 
WHERE 
    p.customerId = t.customerId 
    AND p.balanceSignSince < t.timestamp 
GROUP BY 
    p.customerId; 

Fiddle : http://sqlfiddle.com/#!9/bd0760/13


Оригинал ответа

Это должно работать, чтобы получить временную метку изменения знака:

SELECT 
    c.id as id, 
    MAX(t.timestamp) as balanceSignSince 
FROM 
    transaction as t, 
    customer as c 
WHERE 
    t.customerId = c.id 
    AND SIGN(t.balance) <> SIGN(c.balance) 

Это должно быть выполнено до того, как таблица клиентов будет обновлена ​​с новым балансом. Если у вас есть триггер при трансации: вставьте, вы должны, вероятно, добавить это в запрос, обновляя таблицу клиентов.

+0

У вашего решения есть что-то умное, сравнивающее знак последней транзакции с признаком текущего баланса клиента. Проблема в том, что он не предназначен для запуска в триггере, а как одноразовый «импорт» при добавлении столбца «balanceSignSince», поэтому он должен работать с существующими данными в его текущей форме. Последующие обновления 'balanceSignSince' будут выполняться в коде приложения. – Benjamin

+2

Я создал [SQLFiddle] (http://sqlfiddle.com/#!9/bd0760/2) из ​​вашего решения (добавив отсутствующий «GROUP BY»), но, к сожалению, он не возвращает соответствующие метки времени. – Benjamin

+0

Я обновил ответ, требуя, чтобы это была чисто одноразовая функциональность обновления. – pintxo

Смежные вопросы