MySQL:聚合来自临时 table 的多个对象的更改

MySQL: aggregate changes for multiple objects from a temporary table

我有一个像这样的table,其中保存了每个角色的每个状态变化。

table characters_changes
+----+--------+---------+-------+----------------------+
| id |  rank  |   job   | money |      datetime        |
+----+--------+---------+-------+----------------------+
| 1  |   2    | tailor  |  25   | 2018-06-01 12:30:15  |
| 1  |   3    |  NULL   |   5   | 2018-06-02 10:50:19  |
| 1  |   2    |  NULL   |  -5   | 2018-06-03 18:44:35  |
| 1  |  NULL  | tinker  |  10   | 2018-06-04 04:10:12  |
| 1  |   3    |  NULL   | NULL  | 2018-06-05 17:31:00  |
|  2 |    1   |   spy   |   7   |  2018-06-01 12:30:15 |
|  2 |    2   |   NULL  |  NULL |  2018-06-02 10:50:19 |
|  2 |   NULL |  no job |   7   |  2018-06-03 17:31:00 |
| 3  |   3    | soldier |  12   | 2018-06-01 12:30:15  |
| 3  |   1    |  NULL   | -11   | 2018-06-02 10:50:19  |
+----+--------+---------+-------+----------------------+

NULL表示相应的属性没有变化。 职级和工作的变化意味着一个人换另一个人, 而零钱意味着加减总和(如果不是 NULL)。保证每个字符至少一行,没有任何 NULL-s.

所以我需要得到一个 table,我可以在最后显示每个角色的当前状态。 他们最后的排名,最后一份工作和由此产生的钱数。像这样的table

table characters_status
+----+--------+---------+-------+
| id |  rank  |   job   | money |
+----+--------+---------+-------+
| 1  |   3    | tinker  |  35   |
|  2 |    2   |  no job |   14  |
| 3  |   1    | soldier |   1   |
+----+--------+---------+-------+

更糟糕的是,table characters_changes 是暂时的 table。 其中的日期时间来自另一个 table 事件。 因为它是临时的,所以我只能查询一次。但是可以有任意数量的字符,并且很可能会有更多的列,例如等级和工作。

整个系统需要通过忽略之后的所有更改来提供在任何给定日期时间获取所有角色状态的可能性。但这部分对我来说很容易,所以我把它排除在我的问题范围之外。

我认为这应该可行:

SELECT id
   , CASE WHEN INSTR(ranks, '|') = 0 THEN ranks ELSE LEFT(ranks, INSTR(ranks, '|')-1) END AS rank
   , CASE WHEN INSTR(jobs, '|') = 0 THEN jobs ELSE LEFT(jobs, INSTR(jobs, '|')-1) END AS job
   , monies
FROM
(
   SELECT id
      , GROUP_CONCAT(rank ORDER BY datetime DESC SEPARATOR '|') AS ranks
      , GROUP_CONCAT(job ORDER BY datetime DESC SEPARATOR '|') AS jobs
      , SUM(money) AS monies
   FROM characters_changes
   GROUP BY id
) AS lists
;

从技术上讲,您可以在没有子查询的情况下执行此操作,但为了清楚起见,我将其分解为这种方式。替代方案是这样的表达式:

, CASE COUNT(rank) WHEN 0 THEN NULL WHEN 1 THEN GROUP_CONCAT(rank ORDER BY datetime DESC SEPARATOR '|') ELSE LEFT(GROUP_CONCAT(rank ORDER BY datetime DESC SEPARATOR '|'), INSTR(GROUP_CONCAT(rank ORDER BY datetime DESC SEPARATOR '|'), '|')-1) END AS rank