Câu 1Trong INSERT INTO stock(sku, qty) VALUES ('A001', 15) ON CONFLICT(sku) DO UPDATE SET qty = qty + excluded.qty;, với A001 đã tồn tại (qty 120), qty bằng bao nhiêu sau UPSERT?
UPSERT (ON CONFLICT) và các ứng dụng bulk INSERT
Dùng INSERT … ON CONFLICT(sku) DO UPDATE trên bảng stock để cộng excluded.qty vào A001 đã tồn tại, chèn A006 mới, bảo vệ các dòng hiện có với DO NOTHING, và bulk-UPSERT cả 3 dòng từ stock_in — thực hành trực tiếp.
Dữ liệu sẽ dùng — stock và stock_in
UPSERT (sự pha trộn của UPDATE và INSERT —
thao tác "cập nhật nếu tồn tại, chèn nếu không" trong một câu lệnh) được hiện thực qua cú pháp INSERT … ON CONFLICT(key) DO UPDATE.
Nếu dòng va chạm với khóa chính hoặc ràng buộc UNIQUE, phần cập nhật sẽ chạy; nếu không, nó được chèn nguyên trạng.
ON CONFLICT DO UPDATE — cập nhật nếu tồn tại, chèn nếu không
Viết INSERT INTO table(...) VALUES (...) ON CONFLICT(key_col) DO UPDATE SET col = ... có nghĩa: nếu dòng bạn cố chèn va chạm với khóa được khai báo trong ON CONFLICT (khóa chính hoặc cột UNIQUE), phần cập nhật DO UPDATE sẽ chạy; nếu không có va chạm, dòng được chèn bình thường.
Bên trong DO UPDATE, một tên bảng đặc biệt excluded cho phép bạn tham chiếu "các giá trị của dòng mà bạn đã cố chèn".
Viết qty = qty + excluded.qty và, khi va chạm, qty hiện có sẽ được cộng thêm qty mà bạn cố chèn.
Dùng qty = excluded.qty để ghi đè và qty = qty + excluded.qty để cộng dồn, tùy theo nhu cầu.
-- Va chạm với sku hiện có → DO UPDATE cộng vào qty
INSERT INTO stock(sku, name, qty, price)
VALUES ('A001', 'Pen', 5, 80)
ON CONFLICT(sku) DO UPDATE SET qty = qty + excluded.qty;
-- sku mới → không va chạm, chỉ được chèn
INSERT INTO stock(sku, name, qty, price)
VALUES ('A006', 'Marker', 3, 120)
ON CONFLICT(sku) DO UPDATE SET qty = qty + excluded.qty;
SELECT sku, name, qty FROM stock WHERE sku IN ('A001', 'A006');
DO NOTHING — không làm gì khi va chạm
Khi bạn không muốn cập nhật cũng không muốn chèn — nghĩa là "âm thầm bỏ qua khi va chạm" — hãy dùng ON CONFLICT(key) DO NOTHING.
Không va chạm thì dòng được chèn; khi va chạm thì dòng bị bỏ qua mà không báo lỗi.
Trong ví dụ dưới, cố chèn A002 đã tồn tại với DO NOTHING sẽ va chạm và bị bỏ qua, còn A007 mới sẽ được chèn.
-- Bỏ qua khi va chạm (DO NOTHING)
INSERT INTO stock(sku, name, qty, price)
VALUES ('A002', 'Note', 999, 999)
ON CONFLICT(sku) DO NOTHING;
-- A002 giữ nguyên giá trị ban đầu (qty 60 / price 250)
SELECT sku, name, qty, price FROM stock WHERE sku = 'A002';
UPSERT nhiều dòng — kết hợp bulk INSERT với ON CONFLICT
Khi bạn gắn ON CONFLICT vào một INSERT nhiều dòng — VALUES (...),(...),(...) ngăn cách bằng dấu phẩy — mỗi dòng độc lập "cập nhật khi va chạm, chèn nếu không".
Bạn có thể phản ánh dữ liệu nhập kho trong một câu lệnh, thay thế vòng lặp thủ tục rẽ nhánh giữa UPDATE và INSERT cho mỗi dòng bằng một câu SQL duy nhất.
Ngay cả trong UPSERT nhiều dòng, excluded vẫn có nghĩa là "giá trị mà bạn đã cố chèn cho dòng này", nên viết qty = qty + excluded.qty sẽ áp dụng "cộng vào với các dòng hiện có, chèn nguyên trạng với các dòng mới" theo từng dòng.
Bài tập cuối của bài viết này là UPSERT hàng loạt cả 3 dòng của stock_in (A001 / A004 / A006).
-- UPSERT nhiều dòng: các dòng đã có cộng dồn qty và làm mới price; dòng mới được chèn
INSERT INTO stock(sku, name, qty, price)
VALUES
('A002', 'Note', 10, 260),
('A005', 'Glue', 20, 190),
('A007', 'Ruler', 15, 90)
ON CONFLICT(sku) DO UPDATE
SET qty = qty + excluded.qty,
price = excluded.price;
SELECT sku, name, qty, price FROM stock ORDER BY sku;
Kiểm tra kiến thức
Hãy trả lời từng câu hỏi một.
Câu 2excluded tham chiếu đến cái gì bên trong DO UPDATE của UPSERT?
Câu 3Cú pháp nào bạn dùng để không bao giờ chỉnh sửa dữ liệu hiện có và âm thầm bỏ qua va chạm mà không lỗi?