Menu

Lệnh UPDATE trong SQL với SQLite: WHERE, nhiều cột, FROM

Cách sửa các hàng có sẵn trong SQLite: cú pháp UPDATE, mệnh đề WHERE giúp bạn an toàn, cập nhật nhiều cột và UPDATE ... FROM để sửa dữ liệu giữa các bảng.

Trang này có trình soạn thảo chạy trực tiếp. Sửa, chạy và xem đầu ra ngay lập tức.

UPDATE thay đổi các hàng có sẵn

INSERT thêm hàng mới. UPDATE sửa các hàng đã tồn tại. Cú pháp ngắn và đáng để thuộc lòng:

UPDATE table_name
SET column = value
WHERE condition;

Một ví dụ chạy được:

SET nói cái gì thay đổi. WHERE nói hàng nào. Phần còn lại của bảng được giữ nguyên.

Trong thực tế, WHERE không phải là tùy chọn

Về mặt kỹ thuật, WHERE là tùy chọn. Trên thực tế, bỏ nó đi là cách các kỹ sư mới vào nghề làm hỏng cả buổi chiều của mình:

UPDATE users SET status = 'inactive';
-- every single user is now inactive

Không có bộ lọc nghĩa là mọi hàng đều khớp. SQLite sẽ lặng lẽ làm theo. Luôn viết WHERE trước, rồi mới viết SET: chỉ riêng thói quen đó đã tránh được rất nhiều tai nạn.

Nếu không chắc WHERE của mình đúng, hãy chạy cùng điều kiện đó bằng một lệnh SELECT trước:

Cùng một điều kiện, hai câu lệnh. Lệnh SELECT là lần chạy thử của bạn.

Cập nhật nhiều cột

Ngăn cách các phép gán bằng dấu phẩy trong một SET duy nhất. Một SET, nhiều cột:

Một lần gọi tới cơ sở dữ liệu, một hàng thay đổi, ba cột được cập nhật. Đừng viết ba lệnh UPDATE riêng khi một lệnh là đủ.

Biểu thức ở bên phải dấu =

Giá trị sau = không nhất thiết là một hằng. Nó có thể là bất kỳ biểu thức nào, kể cả biểu thức tham chiếu giá trị hiện tại của chính cột đó:

price * 1.10 đọc giá hiện có, nhân lên và ghi kết quả trở lại. SQLite tính vế phải bằng các giá trị hiện tại của hàng trước khi bất kỳ phép gán nào của câu lệnh này có hiệu lực, nên bạn có thể tham chiếu nhiều cột một cách an toàn:

UPDATE products SET price = price * 1.10, stock = stock + price;
-- 'price' on the right side here is the OLD price, not the just-updated one.

UPDATE ... FROM: lấy giá trị từ bảng khác

Từ SQLite 3.33, UPDATE hỗ trợ mệnh đề FROM để cập nhật giữa các bảng. Đây là cách gọn nhất để đồng bộ dữ liệu giữa các bảng:

Truy vấn con tính tổng cho từng khách hàng; lệnh UPDATE bên ngoài nối các kết quả đó trở lại customers theo id. Không có UPDATE ... FROM, bạn sẽ phải viết một truy vấn con tương quan cho mỗi cột, rối hơn nhiều.

Vài quy tắc cần nhớ:

  • Bảng đích đứng sau UPDATE, không nằm trong danh sách FROM.
  • Mệnh đề WHERE đảm nhận việc nối: ở đây không có từ khóa ON.
  • Nếu phép nối có thể khớp nhiều hơn một hàng trong FROM, kết quả không xác định. Hãy đảm bảo khóa nối cho ra tối đa một hàng khớp với mỗi hàng đích.

RETURNING: xem những gì đã thay đổi

SQLite (từ 3.35) cho phép UPDATE trả về các hàng đã sửa ngay trong cùng câu lệnh. Rất tiện khi ứng dụng cần giá trị sau khi cập nhật mà không phải chạy thêm một lệnh SELECT:

Bạn nhận lại những hàng thực sự bị chạm tới, với giá trị mới của chúng. Bớt được một lần gọi tới cơ sở dữ liệu và loại bỏ cả một nhóm lỗi tranh chấp (race condition) trong code chạy đồng thời. Có hẳn một trang về RETURNING ở phần sau của chương này.

UPDATE OR REPLACE: xử lý xung đột ràng buộc

Nếu lệnh update vi phạm một ràng buộc UNIQUE, hành vi mặc định là hủy câu lệnh và báo lỗi. Mệnh đề OR cho phép bạn chọn cách xử lý khác:

Các lựa chọn là OR ABORT (mặc định), OR REPLACE, OR IGNORE, OR FAIL và OR ROLLBACK. REPLACE là lựa chọn nguy hiểm: nó xóa hàng bị xung đột, và việc này có thể lan dây chuyền qua các khóa ngoại. Chỉ dùng nó khi bạn thực sự muốn nói "nếu đã có hàng mang giá trị duy nhất này thì bỏ hàng đó đi".

Với phần lớn công việc kiểu upsert, cú pháp riêng INSERT ... ON CONFLICT rõ ràng hơn. Có một trang về nó.

Đặt các lệnh update rủi ro trong transaction

Khi bạn sửa nhiều hàng hoặc chạy nhiều lệnh UPDATE cần cùng thành công, hãy đặt chúng trong một transaction. Nếu có gì trục trặc, bạn có thể rollback về trạng thái trước đó:

Nếu câu lệnh thứ hai thất bại (chẳng hạn một ràng buộc bị vi phạm), ROLLBACK sẽ hoàn tác câu lệnh thứ nhất. Không có transaction, bạn sẽ bị kẹt với một giao dịch chuyển tiền dở dang: Ada bị trừ 25, Boris không thay đổi. Transaction có chương riêng ở phần sau; giờ chỉ cần biết chúng tồn tại và các lệnh update hàng loạt gần như luôn nên nằm trong một transaction.

Những lỗi thường gặp

Một danh sách ngắn những điều hay làm người ta vấp:

  • Quên WHERE: cập nhật mọi hàng. Hãy đọc to câu lệnh trước khi chạy.
  • Sai toán tử trong WHERE: WHERE status = NULL không khớp hàng nào. Dùng IS NULL. Ta sẽ nói về điều này trong trang về toán tử.
  • Cập nhật bằng một truy vấn con trả về nhiều hơn một hàng trong khi bạn chỉ mong một hàng. Dùng LIMIT 1 hoặc tổng hợp truy vấn con, nếu không bạn sẽ gặp lỗi hoặc kết quả bất ngờ.
  • Nhầm UPDATE OR REPLACE với UPSERT. OR REPLACE xóa các hàng xung đột. INSERT ... ON CONFLICT DO UPDATE sửa chúng tại chỗ. Hai thao tác khác nhau.

Tiếp theo: DELETE

UPDATE sửa hàng; DELETE xóa hàng. Nguyên tắc cẩn thận với WHERE vẫn áp dụng, và cùng thói quen "chạy SELECT trước" sẽ cứu bạn khỏi những thảm họa tương tự. Đó là nội dung của trang tiếp theo.

Câu hỏi thường gặp

Cú pháp cơ bản của UPDATE trong SQLite là gì?

UPDATE table_name SET column = value WHERE condition;. Mệnh đề SET liệt kê các cột bạn muốn thay đổi cùng giá trị mới. Mệnh đề WHERE chọn những hàng sẽ bị thay đổi: bỏ nó đi thì mọi hàng trong bảng đều bị cập nhật.

Làm sao cập nhật nhiều cột trong một câu lệnh?

Ngăn cách các phép gán bằng dấu phẩy trong mệnh đề SET: UPDATE users SET name = 'Ada', email = 'ada@x.com' WHERE id = 1;. Một câu lệnh, một lần gọi tới cơ sở dữ liệu, một hàng được cập nhật. Đừng lặp lại SET cho từng cột.

SQLite có cập nhật bảng từ một bảng khác được không?

Được. UPDATE ... FROM (có từ SQLite 3.33) cho phép bạn nối một bảng khác hoặc một truy vấn con vào lệnh update. Bạn viết UPDATE target SET col = source.col FROM source WHERE target.id = source.id;. Đây là cách gọn nhất để sao chép giá trị giữa các bảng.

Hình minh họa các ngôn ngữ lập trình của Coddy

Học lập trình cùng Coddy

Bắt đầu