Итак, прежде всего, я хотел бы написать запрос, который произвел строки должны быть обновлены и/или вставки:
WITH performance AS (SELECT 1234 student_id, 5678 course_id, 'Mandatory' enrollment_type, 70 mark, 'ACTIVE' status, 2 VERSION FROM dual UNION ALL
SELECT 1234 student_id, 5678 course_id, 'Optional' enrollment_type, 70 mark, 'HISTORY' status, 1 VERSION FROM dual UNION ALL
SELECT 1234 student_id, 5678 course_id, 'Optional' enrollment_type, NULL mark, 'HISTORY' status, 0 VERSION FROM dual UNION ALL
SELECT 9876 student_id, 4597 course_id, 'Institutional' enrollment_type, 99 mark, 'ACTIVE' status, 1 VERSION FROM dual UNION ALL
SELECT 9876 student_id, 4597 course_id, 'Institutional' enrollment_type, NULL mark, 'HISTORY' status, 0 VERSION FROM dual),
group_enrollments AS (SELECT 4976555 group_id, 1234 student_id, 5678 course_id, 'Mandatory2' enrollment_type FROM dual UNION ALL
SELECT 6399875 group_id, 1234 student_id, 9034 course_id, 'Optional' enrollment_type FROM dual UNION ALL
SELECT 6399875 group_id, 9876 student_id, 4597 course_id, 'Institutional' enrollment_type FROM dual)
-- end of mimicking your tables with data in them
SELECT res.student_id,
res.course_id,
CASE WHEN dummy.id = 1 THEN res.new_enrollment_type
WHEN dummy.id = 2 THEN res.old_enrollment_type
END enrollment_type,
res.mark,
CASE WHEN dummy.id = 1 THEN 'ACTIVE'
WHEN dummy.id = 2 THEN 'HISTORY'
END status,
CASE WHEN dummy.id = 1 THEN res.new_version
WHEN dummy.id = 2 THEN res.old_version
END VERSION
FROM (SELECT ge.student_id,
ge.course_id,
ge.enrollment_type new_enrollment_type,
p.enrollment_type old_enrollment_type,
p.mark,
p.status,
p.version old_version,
nvl(p.version + 1, 0) new_VERSION
-- n.b. this may produce duplicates or unique constraint errors in a concurrent environment
FROM group_enrollments ge
LEFT OUTER JOIN PERFORMANCE p ON ge.student_id = p.student_id
AND ge.course_id = p.course_id
WHERE (p.status = 'ACTIVE' OR p.status IS NULL)
AND (p.enrollment_type != ge.enrollment_type OR p.enrollment_type IS NULL)) res
INNER JOIN (SELECT 1 ID FROM dual UNION ALL
SELECT 2 ID FROM dual) dummy ON dummy.id = 1
OR (dummy.id = 2
AND res.status = 'ACTIVE');
STUDENT_ID COURSE_ID ENROLLMENT_TYPE MARK STATUS VERSION
---------- ---------- --------------- ---------- ------- ----------
1234 5678 Mandatory2 70 ACTIVE 3
1234 9034 Optional ACTIVE 0
1234 5678 Mandatory 70 HISTORY 2
Этот запрос в первую очередь находит любые новые строки (т.е. строки в таблице group_enrollment, которые не имеют строки в таблице производительности) или имеют другой тип регистрации. Это строки, которые нужно вставлять или обновлять.
Как только мы узнаем об этом, мы сможем присоединиться к таблице фиктивных таблиц с двумя строками, чтобы мы всегда присоединялись к первой фиктивной строке независимо от того, нужно ли нам вставлять или обновлять, но мы будем присоединяться только к вторая фиктивная строка, если нам нужно обновить. Это означает, что у нас будет только одна строка для вставки, но две строки для обновления.
Тогда это легкий вопрос вывода правильных значений на основе dummy.id (новые значения для первых фиктивных строк, старые значения для второго фиктивной строки.
После того, как мы сделали это, мы знаем, какие данные должны быть объединены в таблицу производительности, так что теперь оператор слияния будет выглядеть примерно так:
merge into performance tgt
using (SELECT res.student_id,
res.course_id,
CASE WHEN dummy.id = 1 THEN res.new_enrollment_type
WHEN dummy.id = 2 THEN res.old_enrollment_type
END enrollment_type,
res.mark,
CASE WHEN dummy.id = 1 THEN 'ACTIVE'
WHEN dummy.id = 2 THEN 'HISTORY'
END status,
CASE WHEN dummy.id = 1 THEN res.new_version
WHEN dummy.id = 2 THEN res.old_version
END VERSION
FROM (SELECT ge.student_id,
ge.course_id,
ge.enrollment_type new_enrollment_type,
p.enrollment_type old_enrollment_type,
p.mark,
p.status,
p.version old_version,
nvl(p.version + 1, 0) new_VERSION
-- n.b. this may produce duplicates or unique constraint errors in a concurrent environment
FROM group_enrollments ge
LEFT OUTER JOIN PERFORMANCE p ON ge.student_id = p.student_id
AND ge.course_id = p.course_id
WHERE (p.status = 'ACTIVE' OR p.status IS NULL)
AND (p.enrollment_type != ge.enrollment_type OR p.enrollment_type IS NULL)) res
INNER JOIN (SELECT 1 ID FROM dual UNION ALL
SELECT 2 ID FROM dual) dummy ON dummy.id = 1
OR (dummy.id = 2
AND res.status = 'ACTIVE')) src
ON (tgt.student_id = src.student_id AND tgt.course_id = src.course_id AND tgt.status = src.status)
WHEN MATCHED THEN
UPDATE SET tgt.enrollment_type = src.enrollment_type,
tgt.version = src.version
WHEN NOT MATCHED THEN
INSERT (tgt.student_id, tgt.course_id, tgt.enrollment_type, tgt.mark, tgt.status, tgt.version)
VALUES (src.student_id, src.course_id, src.enrollment_type, src.mark, src.status, src.version);
в целях разъяснения, здесь очень простой пример условного дублирования строк (мы могли бы также назовите это частичным перекрестным соединением, поскольку все строки в одной таблице, соединены по меньшей мере, один ряд в другой):
WITH sample_data AS (SELECT 100 ID, NULL status FROM dual UNION ALL -- expect only one row
SELECT 101 ID, 'A' status FROM dual UNION ALL -- expect two rows
SELECT 102 ID, 'B' status FROM dual -- expect only one row
)
SELECT dummy.id dummy_row_id,
sd.id,
sd.status
FROM sample_data sd
INNER JOIN (SELECT 1 ID FROM dual UNION ALL
SELECT 2 ID FROM dual) dummy ON dummy.id = 1
OR (dummy.id = 2
AND sd.status = 'A')
ORDER BY sd.id, dummy.id;
DUMMY_ROW_ID ID STATUS
------------ ---------- ------
1 100
1 101 A
2 101 A
1 102 B
Вы можете видеть, что для ид = 101 строки из «таблицы» sample_data, у нас есть две строки, а две другие идентификаторы только имеют по одной строке.
Надеюсь, что это прояснит вам ситуацию?
Если бы я был вами, я бы написал запрос, который дублировал строки, которые вы собираетесь обновлять, чтобы был один с активным статусом и один с статусом истории - таким образом, вы можете использовать его в своем merge statement, чтобы вставить историческую строку и, при необходимости, обновить активную строку. Если вам нужна помощь в этом, обновите свой вопрос, чтобы включить данные из обеих таблиц вместе с ожидаемым результатом. Таким образом, мы можем проверить все для себя. – Boneist
Обновлено для большей ясности, надеюсь. Вещь, я не буду знать, какие строки я должен обновить, пока не объединю таблицы. Вы имеете в виду, что я должен объединить их, чтобы увидеть, что дублировать и объединить их снова, чтобы обновить активные строки? –
Это помогает - спасибо! Я добавил свой ответ, который показывает, что я первый запрос, чтобы найти строки, которые нуждаются в обновлении, дублировать необходимые строки, а затем использовать этот запрос в операторе слияния. – Boneist