插入数据并用排名填充列
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 and CALL_TIME
对是唯一的。
- 插入的数据中没有
RECORD_RANK
列,但插入时会根据MID and CALL_TIME columns
计算RECORD_RANK
。复制不同CALL_TIME的MID时,MID不同的RECORD_RANK的值会加1,初始值为1。
- insert_table 中最早的行总是晚于 orginal_table 中具有相同 MID 的最新行。
预期的示例结果如下:
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
备注
- mysql 版本:5.5.47-log
如果 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
的新值将由触发器添加。
如果来自 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
原始数据: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 and CALL_TIME
对是唯一的。 - 插入的数据中没有
RECORD_RANK
列,但插入时会根据MID and CALL_TIME columns
计算RECORD_RANK
。复制不同CALL_TIME的MID时,MID不同的RECORD_RANK的值会加1,初始值为1。 - insert_table 中最早的行总是晚于 orginal_table 中具有相同 MID 的最新行。
预期的示例结果如下:
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
备注
- mysql 版本:5.5.47-log
如果 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
的新值将由触发器添加。
如果来自 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