在(我的)SQL中获取上次余额符号更改

时间:2017-02-05 17:14:23

标签: mysql sql

我有一个交易表,记录每个添加到的金额<​​/ em>从客户余额中扣除,并带有新余额:

+----+------------+------------+--------+---------+
| 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 |
+----+------------+------------+--------+---------+

Customer表存储当前余额,如下所示:

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

我想添加一个balanceSignSince列,用于存储余额标志上次更改的时间戳。过渡到否定计为余额变化。

更新后,根据以上数据,Customer表应包含:

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

如何编写一个SQL查询,根据事务表格,在上次更改余额符号时更新每个客户?

我怀疑在没有相当复杂的存储过程的情况下我无法做到这一点,但我很想知道是否有任何聪明的想法出现。

2 个答案:

答案 0 :(得分: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;

小提琴: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)

这需要在使用新余额更新客户表之前执行。如果您有转换触发器:插入您应该将上述内容放入更新客户表的查询中。

答案 1 :(得分: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       |
+------------+----+------------+---------+

然后选择文件的最小值(时间戳),其中rn = 0:

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