Xử lý xung đột khóa chính trong cơ sở dữ liệu Dameng: So sánh MERGE và HINT IGNORE

Trong phát triển ứng dụng cơ sở dữ liệu, xử lý xung đột khóa chính là một vấn đề thường trực. Khi chèn dữ liệu trùng với bản ghi hiện có về khóa chính, ta cần quyết định hành động: bỏ qua, cập nhật hay báo lỗi?

Dameng Database (DM8) cung cấp hai cơ chế hiệu quả để giải quyết vấn đề này: câu lệnh MERGE INTO chuẩn SQL và chỉ dẫn IGNORE_ROW_ON_DUPKEY_INDEX. Bài viết phân tích sâu cả hai phương pháp kèm ví dụ minh họa.

Chuẩn bị môi trường

CREATE TABLE nhan_vien (
    ma_nv INT PRIMARY KEY,
    ten_nv VARCHAR(50) NOT NULL,
    ngay_vao DATE,
    phong_ban VARCHAR(50),
    luong DECIMAL(10,2)
);

INSERT INTO nhan_vien VALUES (1, 'Nguyễn A', '2023-01-15', 'Kỹ thuật', 15000);
INSERT INTO nhan_vien VALUES (2, 'Trần B', '2023-03-20', 'Marketing', 12000);
COMMIT;

Phương pháp 1: MERGE INTO – Giải pháp toàn diện

MERGE INTO thực hiện logic "tồn tại thì cập nhật, chưa có thì chèn" trong một thao tác duy nhất.

Ví dụ cơ bản

MERGE INTO nhan_vien t
USING (
    SELECT 1 AS ma_nv, 'Nguyễn Văn A' AS ten_nv, '2023-01-15' AS ngay_vao, 
           'Kỹ thuật' AS phong_ban, 16000 AS luong FROM DUAL
    UNION ALL
    SELECT 3, 'Lê C', '2024-01-10', 'Tài chính', 11000 FROM DUAL
) nguon
ON (t.ma_nv = nguon.ma_nv)
WHEN MATCHED THEN
    UPDATE SET 
        t.ten_nv = nguon.ten_nv,
        t.phong_ban = nguon.phong_ban,
        t.luong = nguon.luong
WHEN NOT MATCHED THEN
    INSERT (ma_nv, ten_nv, ngay_vao, phong_ban, luong)
    VALUES (nguon.ma_nv, nguon.ten_nv, nguon.ngay_vao, nguon.phong_ban, nguon.luong);

Kết quả:

  • Bản ghi mã 1 được cập nhật (tên thành "Nguyễn Văn A", lương tăng lên 16000)
  • Bản ghi mới mã 3 được chèn vào

Tính năng nâng cao

Cập nhật có điều kiện:

MERGE INTO nhan_vien t
USING (SELECT 1 AS ma_nv, 18000 AS luong_moi FROM DUAL) nguon
ON (t.ma_nv = nguon.ma_nv)
WHEN MATCHED THEN
    UPDATE SET t.luong = nguon.luong_moi
    WHERE t.luong < nguon.luong_moi  -- Chỉ tăng lương
WHEN NOT MATCHED THEN
    INSERT (ma_nv, luong) VALUES (nguon.ma_nv, nguon.luong_moi);

Xóa khi khớp điều kiện:

MERGE INTO nhan_vien t
USING (SELECT 1 AS ma_nv, 'THOAI_VIEC' AS trang_thai FROM DUAL) nguon
ON (t.ma_nv = nguon.ma_nv)
WHEN MATCHED THEN
    UPDATE SET t.phong_ban = 'Đã nghỉ'
    DELETE WHERE nguon.trang_thai = 'THOAI_VIEC';

Phương pháp 2: IGNORE_ROW_ON_DUPKEY_INDEX – Bỏ qua xung đột

Chỉ dẫn này cho phép bỏ qua các dòng gây xung đột khóa chính mà không báo lỗi.

Ví dụ cơ bản

INSERT /*+ IGNORE_ROW_ON_DUPKEY_INDEX(nhan_vien(ma_nv)) */
INTO nhan_vien (ma_nv, ten_nv, phong_ban, luong)
VALUES (1, 'Nguyễn A', 'Kỹ thuật', 15000);  -- Bị bỏ qua do trùng khóa

Chèn hàng loạt

INSERT /*+ IGNORE_ROW_ON_DUPKEY_INDEX(nhan_vien(ma_nv)) */
INTO nhan_vien (ma_nv, ten_nv, phong_ban, luong)
SELECT 1, 'Nguyễn A', 'Kỹ thuật', 15000 FROM DUAL UNION ALL
SELECT 4, 'Phạm D', 'Hành chính', 9000 FROM DUAL UNION ALL
SELECT 2, 'Trần B', 'Marketing', 12500 FROM DUAL;  -- Chỉ chèn được mã 4

Chỉ mục duy nhất đa cột

CREATE UNIQUE INDEX idx_ten_phong ON nhan_vien(ten_nv, phong_ban);

INSERT /*+ IGNORE_ROW_ON_DUPKEY_INDEX(nhan_vien(ten_nv, phong_ban)) */
INTO nhan_vien (ma_nv, ten_nv, phong_ban, luong)
VALUES (5, 'Nguyễn A', 'Kỹ thuật', 16000);  -- Bị bỏ qua do trùng tên + phòng

So sánh và lựa chọn

Đặc điểmMERGE INTOIGNORE HINT
Độ phức tạpCaoThấp
Khả năng cập nhậtCóKhông
Hỗ trợ xóaCóKhông
Tính chuẩn hóaSQL chuẩnĐặc tả Dameng
Phản hồi kết quảChi tiếtChỉ số dòng chèn thực tế

Hướng dẫn lựa chọn

  • Dùng MERGE INTO khi cần cập nhật/xóa hoặc logic phức tạp
  • Dùng IGNORE HINT khi chỉ muốn chèn dữ liệu mới, bỏ qua trùng lặp
  • Ưu tiên MERGE INTO nếu cần khả năng di chuyển giữa các hệ CSDL

Lưu ý quan trọng

Vị trí chỉ dẫn: Phải đặt ngay sau INSERT, không được đặt sau tên bảng.

-- Đúng
INSERT /*+ IGNORE_ROW_ON_DUPKEY_INDEX(...) */ INTO ...

-- Sai
INSERT INTO ... /*+ IGNORE_ROW_ON_DUPKEY_INDEX(...) */ ...

Xử lý NULL: Các giá trị NULL trong cột khóa duy nhất không được coi là trùng nhau.

Giao dịch: MERGE INTO là thao tác nguyên tử – lỗi ở bất kỳ dòng nào sẽ làm toàn bộ thao tác thất bại.

Ví dụ thực tế

Đồng bộ doanh thu hàng ngày:

MERGE INTO doanh_thu_ngay d
USING (
    SELECT ngay_ban, sp_id, SUM(so_tien) AS tong_tien
    FROM du_lieu_tam
    WHERE ngay_ban = TRUNC(SYSDATE)
    GROUP BY ngay_ban, sp_id
) tmp
ON (d.ngay_ban = tmp.ngay_ban AND d.sp_id = tmp.sp_id)
WHEN MATCHED THEN
    UPDATE SET d.tong_tien = d.tong_tien + tmp.tong_tien
WHEN NOT MATCHED THEN
    INSERT (ngay_ban, sp_id, tong_tien) VALUES (tmp.ngay_ban, tmp.sp_id, tmp.tong_tien);

Làm sạch dữ liệu:

INSERT /*+ IGNORE_ROW_ON_DUPKEY_INDEX(du_lieu_sach(id)) */
INTO du_lieu_sach
SELECT * FROM du_lieu_tho
WHERE chat_luong = 'TOT';

Thẻ: Dameng merge IGNORE_ROW_ON_DUPKEY_INDEX sql primary-key-conflict

Đăng vào ngày 4 tháng 10 lúc 14:48