插入数据并用排名填充列

Insert data and fill columns with rank

原始数据:orginal_table

MID STATE CALL_TIME         RECORD_RANK
a   1    2020-12-18 09:00:00        1
a   2    2020-12-19 09:00:00        2
b   1    2020-12-18 09:00:02        1
c   1    2020-12-18 09:00:03        1
c   1    2020-12-19 09:00:03        2
c   1    2020-12-20 09:00:03        3
d   1    2020-12-19 09:00:00        1

我要插入的数据:insert_table

   MID  STATE      CALL_TIME  
   a    2     2020-12-30 09:00:00      
   b    2     2020-12-19 09:00:02   
   c    1     2020-12-21 09:00:03 
   e    1     2020-12-30 09:00:00 
   f    1     2020-12-30 09:00:00 
   f    2     2020-12-31 09:00:00

目标

预期的示例结果如下:

MID  STATE      CALL_TIME         RECORD_RANK
a    1    2020-12-18 09:00:00        1
a    2    2020-12-19 09:00:00        2
b    1    2020-12-18 09:00:02        1
c    1    2020-12-18 09:00:03        1
c    1    2020-12-19 09:00:03        2
c    1    2020-12-20 09:00:03        3
d    1    2020-12-19 09:00:00        1
a    2    2020-12-30 09:00:00        3
b    2    2020-12-19 09:00:02        2  
c    1    2020-12-21 09:00:03        4
e    1    2020-12-30 09:00:00        1
f    1    2020-12-30 09:00:00        1 
f    2    2020-12-31 09:00:00        2

备注

如果 insert_table 中最早的行总是晚于 orginal_table 中具有相同 MID 的最新行,那么您可以使用 BEFORE INSERT 触发器:

CREATE TRIGGER tr_bi_original
BEFORE INSERT
ON orginal_table
FOR EACH ROW
SET NEW.RECORD_RANK = (SELECT COALESCE(COUNT(*), 0) + 1
                       FROM orginal_table
                       WHERE NEW.MID = orginal_table.MID)

创建触发器后,您可以通过

简单地添加新行
INSERT INTO orginal_table
SELECT *, NULL FROM insert_table;

RECORD_RANK 的新值将由触发器添加。

fiddle


如果来自 table 的行将混合并且 RECORD_RANK 中已经存在于 orginal_table 中的某些行必须更改,则无法使用一个查询执行操作 (因为既要插入新行又要更新现有行)。在这种情况下,我建议为 RECORD_RANK 列插入具有任何 (NULL) 值的行,然后重新计算 table.

中所有行的列值

我认为可以在单个 insert 中处理逻辑,即使在 MySQL 5.x.

中也是如此

目标排名是相同 mid 的目标 table 中已存在的行数加上源 table 中的 mid 行数] 在当前行之前。您可以使用相关子查询来计算它:

insert into orginal_table (mid, state, call_time, record_rank)
select mid, state, call_time,
    1 + (
        select count(*)
        from orginal_table o
        where o.mid = i.mid
    ) + (
        select count(*) 
        from insert_table i1 
        where i1.mid = i.mid and i1.call_time < i.call_time
    ) as record_rank
from insert_table i

这假定所有新行都比现有行更新,如您的问题中所述。但是如果你不想要,这是对第一个子查询的简单修复:

(
    select count(*)
    from orginal_table o
    where o.mid = i.mid and o.call_time < i.call_time
)

这里是 a demo 基于 Akina 构建的 nice 测试用例。

旁注:在 MySQL 8.0 中,我们将使用 window 函数代替第二个子查询,这将使查询更加高效:

insert into orginal_table (mid, state, call_time, record_rank)
select mid, state, call_time,
    row_number() over(partition by mid order by call_time) 
    + (
        select count(*)
        from orginal_table o
        where o.mid = i.mid
    ) as record_rank
from insert_table i