исходные данные: original_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, но RECORD_RANK будет рассчитываться на основе MID and CALL_TIME columns при вставке. При дублировании MID с другим CALL_TIME значение RECORD_RANK с MID будет добавлено на 1. Начальное значение равно 1.Ожидаемый результат примера, как показано ниже:
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
Примечание
Между прочим, по соглашению термин «id» подразумевает суррогатный ПЕРВИЧНЫЙ КЛЮЧ. Это не тот случай, поэтому, возможно, стоит переименовать этот столбец.
@ArunPalanisamy Нет, любое значение идентификатора может быть продублировано.
@Strawberry, я изменил «ID» на «MID».
Всегда ли самая ранняя строка в insert_table позже последней строки в orginal_table с тем же MID? то есть значения RECORD_RANK, уже присутствующие в orginal_table, никогда не будут пересчитываться?
@Akina да, самая ранняя строка в insert_table всегда позже последней строки в orginal_table с тем же MID.
Возможно, что еще более важно, триггер Акины дает возможность установить MID на будущее INSERTs. (Он не заботится об изменении ранга из-за удаления или обновления.)






Если самая первая строка в 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 будут добавлены триггером.
Если строки из обеих таблиц будут смешиваться и RECORD_RANK для некоторых строк, которые уже присутствуют в orginal_table, необходимо изменить, то операцию нельзя выполнить с помощью одного запроса (поскольку требуется как вставка для новых строк, так и обновление для существующих). В этом случае я рекомендую вставить строки с любым (NULL) значением для столбца RECORD_RANK, а затем пересчитать значение столбца для всех строк в таблице.
Я думаю, мы просто видим это по-разному
Почему? ОП будет работать по принципу предвзятости подтверждения.
@ Акина, нужно ли использовать триггер?
@jack Нет, конечно, вы можете использовать любой другой метод. Но его использование упрощает процесс, кажется.
@Akina - Ваша техника тесака может быть улучшена, если заменить COUNT(*) на MAX(mid)?
@РикДжеймс ??? ... рабочий пример доступна - вы можете попробовать.
@Akina «INSERT INTO orginal_table» без (column1, column2, column3, ...) кажется рискованным, если я просто хочу вставить определенные столбцы, такие как скрипт GMB. А я не знаю вашего скрипта, потому что не умею пользоваться триггером. Не могли бы вы объяснить это?
@Jack кажется рискованным. Если ваши данные для вставки могут быть неправильными (могут иметь неправильную структуру), добавьте список столбцов - никаких проблем. Я не знаю, как использовать триггер. Скрипка полностью это демонстрирует. Пожалуйста, не задавайте общий вопрос, указывайте на непонятное для вас место.
Я думаю, что можно обрабатывать логику в одном insert, даже в MySQL 5.x.
Целевой ранг — это количество строк, которые уже существуют в целевой таблице для одного и того же mid, плюс количество 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
)
Вот демо, основанное на хорошем тестовом примере, созданном Akina.
Примечание: в MySQL 8.0 вместо второго подзапроса мы использовали оконную функцию, что сделало бы запрос более эффективным:
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
Если call_time всегда увеличивается, я не вижу необходимости хранить record_rank. Просто рассчитайте, когда вам это нужно