2017-01-16 5 views
0

私はここでいくつかの同様の質問を見つけましたが、どれも実際に自分の状況に合わないようです。私が働いているOracle SQL - 一致した場合は、更新して挿入してください

の表は、このようなもので、それが彼らのコースの学生のパフォーマンスを記録し、テーブルです:

| STUDENT_ID | COURSE_ID | ENROLLMENT_TYPE | MARK | STATUS | VERSION | 
|   |   |     |  |   |   | 
| 1234  | 5678  | Mandatory  | 70 | ACTIVE | 2  | 
| 1234  | 5678  | Optional  | 70 | HISTORY | 1  | 
| 1234  | 5678  | Optional  | null | HISTORY | 0  | 
| 9876  | 4597  | Institutional | 99 | ACTIVE | 1  | 
| 9876  | 4597  | Institutional | null | HISTORY | 0  | 

私は別のテーブルでこれをマージする必要があり、それはの入学者を追跡します必要に応じて、私は行を挿入または更新することができるように、彼らのグループに基づいて学生:一部の学生はに在籍ますので、

| GROUP_ID | STUDENT_ID | COURSE_ID | ENROLLMENT_TYPE | 
| 4976555 | 1234  | 5678  | Mandatory  | 
| 6399875 | 1234  | 9034  | Optional  | 
| 6399875 | 9876  | 4597  | Institutional | 

かいつまんで、私は、入学式が同じであるか、変更されているかどうかを確認する必要があります彼らが参加しているグループに基づいたコースですが、グループは一晩で変更できます。

ここまでは問題ありませんが、行が一致する場合は、更新する行をコピーして「履歴」として設定する必要があります。これにより、更新されたすべての更新を記録できますある行に作られた。現時点で

、私は私は直前に「HISTORY」のものと、すべての行をコピーするには素晴らしいアイデアだと思った前に、コードのこの部分に働いていた人

MERGE INTO performance USING group_enrollments 
ON performance.STUDENT_ID = group_enrollments.STUDENT_ID 
AND performance.COURSE_ID = group_enrollments.COURSE_ID 
WHEN MATCHED THEN UPDATE ...... 
WHEN NOT MATCHED THEN INSERT ...... 

典型的なを持っていますマージを行っていますが、毎晩このプロシージャが実行され、毎回150,000以上の行が書き込まれるため、問題が発生しています。 アイデア

16/01 12.34:、テーブルとその関係

+0

私があなただったら、更新する予定の行を複製するクエリを作成して、アクティブステータスと履歴ステータスを持つ行を作成しました。マージステートメントを使用して履歴行を挿入し、必要に応じてアクティブ行を更新します。これに助けが必要な場合は、質問を更新して、両方のテーブルのデータと予想される出力を含めてください。そうすれば、自分たちのために物事をテストすることができます。 – Boneist

+0

もっと明瞭にするために更新されました。 これは、テーブルをマージするまで、どの行を更新する必要があるのか​​わかりません。アクティブな行を更新するために何を複製して再度マージするかを見るためにそれらをマージする必要がありますか? –

+0

これは役に立ちます - ありがとう!私は自分の答えを追加しました。まず、更新が必要な行を見つけ出し、必要な行を複製してから、そのクエリをマージステートメントで使用することを示します。 – Boneist

答えて

1

オクラホマについての詳細情報を更新しましたので、最初のオフ、私は行が更新され、および/または挿入されるように作らクエリを記述します。

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テーブルの行)または異なるenrollment_typeを持つ行を見つけます。これらは、挿入または更新が必要な行です。

これが分かったら、ダミーの2行テーブルテーブルを結合して、挿入または更新の必要性にかかわらず最初のダミー行に常に参加しますが、更新が必要な場合は2番目のダミー行です。これは、挿入のための行は1つだけですが、更新のための行は2つしかないことを意味します。

そして、それはdummy.idに基づいて正しい値(第1のダミー行の新しい値、第2のダミー行の古い値を出力する簡単な問題だ。

私たちが知っている、ことをやったら我々はまた、可能性(

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); 
明確化の目的のために

が、ここでは、行の条件付き重複の非常に単純な例です:どのようなデータは、パフォーマンステーブルにマージする必要があり、今MERGE文は次のようになりますそれは部分的なクロス結合と呼ばれ、すべての行一つのテーブルで)他の少なくとも一つの列に結合されている:

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 

あなたがSAMPLE_DATA「テーブル」から= 101行IDのために、我々は、2つの行が、他の2つのIDのみを持っていることを見ることができますそれぞれに1つの行があります。

あなたのために物事を明確にすることを望みますか?

+0

あなたは本当にそのupvoteを得ました。 –

+0

私はまだSQLに新しいので、私はここであなたのポイントを持っていないと思う。 これは、一致するものが見つかると、新しい行をどのように作成しますか? –

+0

私は内部のサブクエリにジョインするダミーのサブクエリが表示されますか?これは2行の結果セットです。結合条件 'dummy.id = 1'は、ダミーサブクエリからresサブクエリのすべての単一の行に常に最初の行を結合することを保証します。 'または(dummy.id = 2およびres.status = 'ACTIVE')'条件は、res.status = 'ACTIVE'の場合に2番目のダミー行にのみ参加することを意味します。 res.status列は、既にstudent_idとcourse_idの行がパフォーマンス表にある場合にのみ挿入されるため、更新された行と履歴行の2つの行が必要です。理にかなっている? – Boneist

関連する問題