Menu
ESC

Nhập từ khóa để tìm kiếm

↑↓ Di chuyển
Enter Mở
ESC Đóng

Đang tải...

INSERT, UPDATE, DELETE: BA cần hiểu DML

SQL and Data Analysis cho BA Bài 53/60

Mở đầu — vì sao bài này quan trọng

Suốt cả khóa học này, bạn đã quen với việc chỉ "đọc" dữ liệu: SELECT ra, lọc bằng WHERE, gom bằng GROUP BY, ghép bằng JOIN. Đó là 90% công việc thường ngày của một BA, và điều đó hoàn toàn đúng. Phần lớn thời gian, bạn nên chỉ có quyền đọc trên hệ thống production để khỏi vô tình làm hỏng dữ liệu thật.

Nhưng có một sự thật mà nhiều BA né tránh: bạn sẽ không thể tránh được DML mãi. DML — viết tắt của Data Manipulation Language — là nhóm câu lệnh dùng để thay đổi dữ liệu trong bảng: INSERT (thêm dòng mới), UPDATE (sửa dòng đã có), DELETE (xóa dòng). Sớm muộn gì bạn cũng sẽ chạm vào chúng trong những tình huống rất thực tế:

  • Bạn cần tự dựng dữ liệu test trong sandbox để kiểm tra một báo cáo hay một logic nghiệp vụ trước khi dev triển khai.
  • Có một lô dữ liệu bị sai (sai mã tỉnh, sai trạng thái đơn hàng) và bạn được giao viết câu lệnh fix để team data engineer review rồi chạy.
  • Bạn ngồi cạnh kỹ sư trong một buổi xử lý sự cố, và bạn cần đọc hiểu được câu UPDATE họ sắp chạy để xác nhận nó đúng nghiệp vụ.
  • Bạn viết migration script cho một schema mới trong dự án nhỏ mà bạn vừa là BA vừa kiêm "nửa kỹ thuật".
Điều nguy hiểm nhất với DML không phải là cú pháp khó — cú pháp DML thực ra rất đơn giản. Nguy hiểm nằm ở chỗ: một câu SELECT viết sai chỉ trả ra kết quả sai, bạn xem rồi bỏ. Nhưng một câu UPDATE hay DELETE viết sai sẽ thay đổi vĩnh viễn dữ liệu — và nếu thiếu mệnh đề WHERE, nó có thể quét sạch hoặc ghi đè toàn bộ bảng trong một phần nghìn giây. Bài học này sẽ cho bạn đủ hiểu biết để dùng DML một cách an toàn, đọc được câu lệnh người khác viết, và quan trọng nhất là biết khi nào không nên tự tay chạy.

Khái niệm cốt lõi

DML đứng cạnh hai nhóm câu lệnh khác mà bạn nên phân biệt được. DDL (Data Definition Language: CREATE, ALTER, DROP) định nghĩa cấu trúc bảng. DCL (Data Control Language: GRANT, REVOKE) quản lý quyền. Còn DML thao tác trên nội dung dữ liệu. Là BA, bạn chủ yếu cần thành thạo DML ở mức đọc-hiểu và viết cẩn thận.

INSERT — thêm dòng mới

INSERT đưa dữ liệu mới vào bảng. Cú pháp cơ bản:

INSERT INTO khach_hang (ho_ten, sdt, thanh_pho, ngay_tao)
VALUES ('Nguyễn Văn An', '0901234567', 'Hà Nội', '2026-06-27');

Một mẹo quan trọng: luôn liệt kê tên cột rõ ràng trong ngoặc, đừng dựa vào thứ tự cột ngầm định. Nếu ai đó thêm cột mới vào bảng sau này, câu INSERT không ghi tên cột của bạn sẽ vỡ ngay. Bạn cũng có thể chèn nhiều dòng một lần:

INSERT INTO khach_hang (ho_ten, sdt, thanh_pho)
VALUES
  ('Trần Thị Bình', '0912000001', 'Đà Nẵng'),
  ('Lê Hữu Cường', '0912000002', 'TP.HCM');

Và mạnh hơn cả — bạn có thể chèn từ một câu SELECT, cực kỳ hữu ích khi dựng bảng test từ dữ liệu thật:

INSERT INTO khach_hang_test (ho_ten, sdt, thanh_pho)
SELECT ho_ten, sdt, thanh_pho
FROM khach_hang
WHERE thanh_pho = 'Hà Nội'
LIMIT 100;

UPDATE — sửa dòng đã có

UPDATE thay đổi giá trị của các dòng đã tồn tại. Đây là câu lệnh nguy hiểm nhất vì bản chất "ghi đè":

UPDATE don_hang
SET trang_thai = 'da_giao'
WHERE ma_don = 'DH20260627001';

Mệnh đề SET chỉ ra cột nào đổi sang giá trị gì. Mệnh đề WHERE quyết định những dòng nào bị ảnh hưởng. Nếu bạn quên WHERE, toàn bộ bảng sẽ bị đổi. Hãy khắc cốt ghi tâm điều này. Bạn cũng có thể cập nhật nhiều cột cùng lúc và dùng biểu thức:

UPDATE san_pham
SET gia_ban = gia_goc * 0.8,
    dang_giam_gia = TRUE
WHERE danh_muc = 'thoi_trang';

DELETE — xóa dòng

DELETE loại bỏ dòng khỏi bảng:

DELETE FROM gio_hang
WHERE cap_nhat_lan_cuoi < '2026-01-01';

Cũng như UPDATE, thiếu WHERE nghĩa là xóa sạch bảng. Lưu ý phân biệt: DELETE FROM ban (không WHERE) xóa từng dòng và có thể rollback; còn TRUNCATE TABLE ban là lệnh DDL xóa cực nhanh, không log từng dòng, thường không rollback được. BA gần như không bao giờ nên động đến TRUNCATE trên dữ liệu thật.

Transaction — phao cứu sinh của bạn

Đây là khái niệm cứu mạng mà mọi BA phải biết trước khi gõ DML. Một transaction cho phép bạn nhóm các thay đổi lại, xem trước kết quả, rồi mới quyết định commit (xác nhận) hay rollback (hủy bỏ):

BEGIN;  -- bắt đầu transaction

UPDATE don_hang SET trang_thai = 'huy' WHERE ma_khach = 'KH00123';

-- Kiểm tra: lệnh trên có ảnh hưởng đúng số dòng mong đợi không? -- Nếu sai → ROLLBACK; -- Nếu đúng → COMMIT;

Trong PostgreSQL và MySQL (InnoDB), nếu bạn ROLLBACK trước khi COMMIT, mọi thay đổi biến mất như chưa từng xảy ra. Đây là tấm lưới an toàn quan trọng nhất khi bạn buộc phải sửa dữ liệu.

Tình huống thực tế

Tình huống 1 — Sàn TMĐT mô phỏng kiểu Tiki: dựng dữ liệu test cho báo cáo cohort

Bạn là BA tại một công ty bán lẻ online. Team data engineer vừa dựng bảng cohort mới, và bạn cần kiểm tra báo cáo retention (đã học ở Bài 29) chạy đúng không. Nhưng môi trường staging trống trơn — không có đơn hàng nào để báo cáo có gì hiển thị.

Bạn được cấp quyền ghi trên schema sandbox. Bạn viết:

INSERT INTO sandbox.don_hang (ma_khach, ngay_dat, gia_tri)
VALUES
  ('KH001', '2026-01-05', 250000),
  ('KH001', '2026-02-10', 180000),  -- KH001 quay lại tháng 2
  ('KH002', '2026-01-08', 500000),  -- KH002 chỉ mua 1 lần
  ('KH003', '2026-01-15', 320000),
  ('KH003', '2026-03-20', 410000);  -- KH003 quay lại tháng 3

Với 5 dòng được thiết kế có chủ đích, bạn biết chính xác kết quả retention phải ra: tháng 1 có 3 khách, tháng 2 giữ lại 1 (33%), tháng 3 giữ lại 1. Khi chạy báo cáo mà con số khớp, bạn yên tâm logic đúng.

Bài học rút ra: dữ liệu test do BA tự dựng có một sức mạnh mà dữ liệu thật không có — bạn biết trước đáp án. Đây là cách kiểm thử báo cáo đáng tin cậy nhất, và nó đòi hỏi bạn thành thạo INSERT. Luôn làm việc này trên schema sandbox/staging, không bao giờ trên production.

Tình huống 2 — Ví điện tử mô phỏng kiểu MoMo: fix dữ liệu sai qua change request

Một sự cố xảy ra: do lỗi tích hợp của một đối tác, 1.240 giao dịch nạp tiền trong ngày 25/06 bị gán sai loai_giao_dich = 'rut_tien' thay vì 'nap_tien'. Báo cáo dòng tiền vì thế lệch hẳn. Sếp giao bạn — BA hiểu nghiệp vụ nhất về luồng này — viết câu lệnh fix để kỹ sư review và chạy.

Bạn không chạy thẳng. Bạn làm theo quy trình change request an toàn. Trước tiên, đếm để biết phạm vi:

SELECT COUNT(*)
FROM giao_dich
WHERE doi_tac = 'PARTNER_X'
  AND DATE(thoi_gian) = '2026-06-25'
  AND loai_giao_dich = 'rut_tien'
  AND so_tien > 0;
-- Kết quả: 1240 → khớp với con số sự cố báo cáo

Sau khi xác nhận đúng 1.240 dòng, bạn soạn câu UPDATE bọc trong transaction:

BEGIN;

UPDATE giao_dich SET loai_giao_dich = 'nap_tien' WHERE doi_tac = 'PARTNER_X' AND DATE(thoi_gian) = '2026-06-25' AND loai_giao_dich = 'rut_tien' AND so_tien > 0;

-- Kỹ sư kiểm tra: "UPDATE 1240" — đúng số dòng kỳ vọng → COMMIT; -- Nếu hiện "UPDATE 5800" → có gì đó sai → ROLLBACK ngay.

Điểm tinh tế: mệnh đề WHERE của bạn dùng đúng các điều kiện đã đếm ở bước trên, không thêm không bớt. Con số dòng bị ảnh hưởng phải khớp tuyệt đối với con số bạn đã SELECT COUNT(*) ra trước đó.

Bài học rút ra: quy trình vàng để sửa dữ liệu là đếm trước — sửa sau — đối chiếu số dòng. Câu WHERE trong UPDATE phải y hệt câu WHERE trong SELECT COUNT(*) kiểm tra. Trong lĩnh vực fintech, mọi câu DML đụng đến tiền đều phải qua review và để lại dấu vết — đừng bao giờ tự ý chạy một mình.

Tình huống 3 — Ứng dụng gọi xe mô phỏng kiểu Be/Grab: dọn dữ liệu rác trong staging

Trong môi trường staging của app gọi xe, suốt mấy tháng test, bảng chuyen_di tích tụ hàng chục nghìn chuyến giả do automation test sinh ra, khiến môi trường nặng nề và báo cáo demo cho khách hàng bị nhiễu. Bạn được giao dọn dẹp các chuyến test cũ.

Trước khi DELETE, bạn luôn "thử" bằng SELECT với đúng mệnh đề WHERE định dùng:

-- Bước 1: XEM TRƯỚC thứ sẽ bị xóa
SELECT COUNT(*), MIN(thoi_gian_dat), MAX(thoi_gian_dat)
FROM chuyen_di
WHERE la_chuyen_test = TRUE
  AND thoi_gian_dat < '2026-04-01';
-- Kết quả: 48.500 dòng, từ 2025-11 đến 2026-03 → hợp lý

Chỉ khi con số và khoảng thời gian trông hợp lý, bạn mới đổi SELECT thành DELETE, vẫn bọc transaction:

BEGIN;
DELETE FROM chuyen_di
WHERE la_chuyen_test = TRUE
  AND thoi_gian_dat < '2026-04-01';
-- "DELETE 48500" → khớp → COMMIT;

Bài học rút ra: với DELETE, kỹ thuật "viết SELECT trước, đổi thành DELETE sau" là tấm khiên hiệu quả nhất. Bạn nhìn tận mắt thứ mình sắp xóa trước khi xóa thật. Điều kiện la_chuyen_test = TRUE cũng cho thấy giá trị của việc thiết kế dữ liệu có cờ phân biệt test/thật ngay từ đầu.

Hướng dẫn từng bước

Đây là quy trình an toàn bạn nên áp dụng mỗi khi buộc phải chạy DML:

  • Xác nhận đúng môi trường. Nhìn kỹ bạn đang kết nối tới database nào. sandbox/staging thì thoải mái; production thì dừng lại và tự hỏi mình có thực sự nên là người chạy không. Tốt nhất là dùng connection riêng chỉ-staging để tránh nhầm.
  • Viết SELECT thăm dò trước. Với UPDATE/DELETE, luôn chạy SELECT COUNT() (hoặc SELECT xem mẫu) với chính xác mệnh đề WHERE bạn định dùng. Ghi nhớ con số dòng.
  • Mở transaction bằng BEGIN;. Đừng để DML "trần" không có lưới an toàn.
  • Chạy câu DML. Đọc kỹ thông báo: "UPDATE 1240", "DELETE 48500". So với con số ở bước 2.
  • Đối chiếu rồi quyết định. Số dòng khớp kỳ vọng → COMMIT;. Lệch một chút thôi → ROLLBACK; và điều tra lại WHERE.
  • Kiểm tra hậu kỳ. Sau COMMIT, chạy lại một SELECT để xác nhận dữ liệu giờ đã đúng như mong muốn.
  • Để lại dấu vết. Ghi vào change ticket: câu lệnh đã chạy, số dòng ảnh hưởng, thời điểm, người review. Trong môi trường chuyên nghiệp đây không phải tùy chọn.

Lỗi thường gặp & mẹo

Quên mệnh đề WHERE. Đây là tai nạn kinh điển và tàn khốc nhất. UPDATE don_hang SET trang_thai = 'huy'; sẽ hủy mọi đơn hàng trong hệ thống. Mẹo: tập thói quen gõ WHERE ngay sau khi gõ tên bảng, trước cả khi gõ SET. Một số công cụ (như MySQL Workbench) có "safe update mode" chặn UPDATE/DELETE không có WHERE trên khóa chính — hãy bật nó lên.

Chạy DML mà không có transaction. Không BEGIN/COMMIT nghĩa là sai một phát là mất luôn, không cứu được. Luôn bọc transaction khi sửa dữ liệu thật.

WHERE của UPDATE rộng hơn WHERE của SELECT kiểm tra. Bạn SELECT COUNT(*) ra 1.240 nhưng câu UPDATE lại thiếu một điều kiện nên đụng 5.800 dòng. Luôn copy-paste y hệt mệnh đề WHERE giữa hai câu, đừng gõ lại từ trí nhớ.

Nhầm NULL trong điều kiện. WHERE thanh_pho != 'Hà Nội' sẽ bỏ sót các dòng có thanh_phoNULL, vì so sánh với NULL luôn ra "unknown". Nếu muốn gồm cả NULL, viết WHERE thanh_pho != 'Hà Nội' OR thanh_pho IS NULL. (Bạn đã học NULL handling ở Bài 7 — nó cực kỳ quan trọng trong DML.)

Đụng phải ràng buộc khóa ngoại khi DELETE. Xóa một khách hàng đang có đơn hàng tham chiếu tới sẽ bị database từ chối (foreign key constraint), hoặc tệ hơn — nếu cấu hình CASCADE — sẽ xóa lan sang cả các bảng con. Hiểu quan hệ bảng trước khi xóa.

Mẹo vàng: với BA, đường an toàn nhất để "sửa data" thường không phải tự chạy UPDATE, mà là viết câu lệnh đã được kiểm chứng rồi gửi cho data engineer chạy qua change request. Bạn đóng góp hiểu biết nghiệp vụ (biết cái gì sai và phải đổi thành gì), họ đóng góp sự an toàn vận hành. Đây là sự phân vai lành mạnh nhất.

Bài tập thực hành

Hãy thực hành trên một schema sandbox (đừng dùng dữ liệu thật):

  • INSERT: Tạo bảng khach_hang_test và chèn 5 khách hàng với các thành phố khác nhau, trong đó có ít nhất một dòng để trống cột sdt (tức NULL). Dùng cú pháp chèn nhiều dòng trong một câu lệnh.
  • INSERT từ SELECT: Giả sử có bảng khach_hang thật, viết câu lệnh chép 50 khách ở TP.HCM sang bảng khach_hang_test.
  • UPDATE an toàn: Bạn cần đổi trang_thai của tất cả đơn hàng "cho_xac_nhan" tạo trước ngày 01/06/2026 thành "qua_han". Viết đủ ba câu: (a) SELECT COUNT(*) thăm dò, (b) BEGIN; + UPDATE, (c) câu kiểm tra sau khi đổi. Tự hỏi: nếu số dòng không khớp thì bạn làm gì?
  • DELETE có xem trước: Viết một câu SELECT xem trước các dòng test cũ sẽ bị xóa, rồi chuyển thành DELETE tương ứng, bọc trong transaction.
  • Tình huống xử lý: Một đồng nghiệp gửi bạn câu lệnh DELETE FROM nguoi_dung WHERE trang_thai = 'inactive' để review. Hãy chỉ ra ít nhất 2 câu hỏi an toàn bạn cần đặt ra trước khi đồng ý cho chạy (gợi ý: môi trường nào? đã đếm số dòng chưa? có khóa ngoại không? có transaction không?).

Tóm tắt

DML — INSERT, UPDATE, DELETE — là nhóm câu lệnh thay đổi dữ liệu, khác hẳn với SELECT chỉ đọc mà bạn dùng hằng ngày. Là BA, bạn không cần dùng DML thường xuyên, nhưng bạn phải hiểu chúng để dựng dữ liệu test trong sandbox, viết câu lệnh fix data qua change request, và đọc hiểu được câu lệnh kỹ sư sắp chạy.

Ba điều cốt lõi cần khắc ghi: một, UPDATE/DELETE mà thiếu WHERE sẽ tàn phá cả bảng — luôn gõ WHERE trước. Hai, quy trình vàng là đếm trước, sửa sau, đối chiếu số dòng, với mệnh đề WHERE y hệt nhau giữa câu kiểm tra và câu thực thi. Ba, luôn bọc thao tác trong transaction (BEGINCOMMIT/ROLLBACK) để có lưới an toàn. Và mẹo lớn nhất: trên dữ liệu production, con đường khôn ngoan của BA thường là viết câu lệnh, không tự chạy — bạn góp hiểu biết nghiệp vụ, để kỹ sư đảm bảo an toàn vận hành. Hiểu DML không phải để bạn thành người xóa nhầm dữ liệu, mà để bạn thành người ngăn việc đó xảy ra.