aedev.net
  • Trang chủ
  • AI
  • Mobile
  • Flutter
  • React Native
  • Backend
  • DevOps
  • Hướng dẫn
No Result
View All Result
SAVED POSTS
aedev.net
  • Trang chủ
  • AI
  • Mobile
  • Flutter
  • React Native
  • Backend
  • DevOps
  • Hướng dẫn
No Result
View All Result
aedev.net
No Result
View All Result
Minh họa kiến trúc backend API gọn gàng cho sản phẩm nhỏ

SQLite REPLACE: cột bị bỏ qua có thể mất giá trị cũ

Suong Nguyen by Suong Nguyen
06/11/2026
in Backend
0
585
SHARES
3.2k
VIEWS
Summarize with ChatGPTShare to Facebook

Bạn muốn cập nhật tên hiển thị theo email và chọn INSERT OR REPLACE cho gọn. Request chỉ gửi email cùng tên mới, nhưng ghi chú cũ trong database biến thành giá trị mặc định. REPLACE khi gặp xung đột khóa duy nhất có cơ chế khác một UPDATE chỉ sửa các cột được chỉ định.

Bài đối chiếu tài liệu SQLite ngày 06/11/2026 và chạy bằng Python 3.9.6, SQLite 3.54.0 trong bộ nhớ. Đây là fixture độc lập, chưa chạy trên database ứng dụng, ORM hoặc PostgreSQL. Cú pháp UPSERT dùng ở đây có trong SQLite từ 3.24.0; cần kiểm tra SQLite thật sự được đóng gói trên thiết bị hoặc server của bạn.

Xác định cột nào cần được giữ lại

Trong demo có email là khóa duy nhất, display_name là cột muốn đổi, note là thông tin phải giữ nếu request không đề cập. Khi xung đột UNIQUE hoặc PRIMARY KEY, REPLACE loại hàng gây xung đột trước khi tiếp tục ghi hàng mới. Cột không nằm trong INSERT sẽ theo quy tắc của lần chèn, không tự lấy giá trị từ hàng cũ.

Với UPSERT, ON CONFLICT chỉ rõ khóa gây xung đột và DO UPDATE chỉ rõ các cột cần sửa. Bạn có thể giữ note bằng cách không đưa note vào tập gán của UPDATE.

1. Tái hiện bằng hai database giống nhau

Chạy nguyên đoạn Python sau. Mỗi cách bắt đầu từ một database mới, nên phép so sánh không chịu ảnh hưởng lần ghi trước:

import sqlite3

def make_db():
    db = sqlite3.connect(":memory:")
    db.executescript("""
        CREATE TABLE demo_profiles (
            id INTEGER PRIMARY KEY,
            email TEXT NOT NULL UNIQUE,
            display_name TEXT NOT NULL,
            note TEXT NOT NULL DEFAULT 'new row'
        );
        INSERT INTO demo_profiles VALUES
            (10, '[email protected]', 'Old name', 'keep this note');
    """)
    return db

replace_db = make_db()
replace_db.execute("""
    INSERT OR REPLACE INTO demo_profiles(email, display_name)
    VALUES (?, ?)
""", ("[email protected]", "New name"))
replaced = replace_db.execute("""
    SELECT display_name, note FROM demo_profiles
""").fetchone()
assert replaced == ("New name", "new row")

upsert_db = make_db()
upsert_db.execute("""
    INSERT INTO demo_profiles(email, display_name) VALUES (?, ?)
    ON CONFLICT(email) DO UPDATE
    SET display_name = excluded.display_name
""", ("[email protected]", "New name"))
updated = upsert_db.execute("""
    SELECT id, display_name, note FROM demo_profiles
""").fetchone()
assert updated == (10, "New name", "keep this note")
print("REPLACE:", replaced)
print("UPSERT:", updated)

Hai tên hiển thị đều được đổi, nhưng note khác nhau. Fixture chủ động kiểm tra cả id ở nhánh UPSERT để xác nhận hàng cũ được cập nhật trong ca này. Không dựa vào một giá trị id mới cụ thể ở nhánh REPLACE để suy luận quy tắc cấp khóa cho mọi schema.

excluded.display_name tham chiếu giá trị mà INSERT định ghi. Nó không phải alias của hàng cũ. Khi thêm nhiều cột vào UPDATE, hãy xác định rõ cột lấy từ request và cột lấy từ giá trị đang lưu.

2. Nhánh không xung đột vẫn là INSERT

Nối tiếp file để kiểm tra email chưa có. Các database chỉ tồn tại trong bộ nhớ:

upsert_db.execute("""
    INSERT INTO demo_profiles(email, display_name) VALUES (?, ?)
    ON CONFLICT(email) DO UPDATE
    SET display_name = excluded.display_name
""", ("[email protected]", "Another dev"))
new_note = upsert_db.execute("""
    SELECT note FROM demo_profiles WHERE email = ?
""", ("[email protected]",)).fetchone()
assert new_note == ("new row",)
replace_db.close()
upsert_db.close()

Giữ note khi UPDATE không có nghĩa mọi hàng mới đều có note cũ. Ở nhánh INSERT, note vẫn cần giá trị request, default hoặc một quy tắc nghiệp vụ khác đã xác định.

Kiểm tra trước khi đổi câu lệnh trong ứng dụng

Liệt kê các cột request có thể bỏ qua và định nghĩa “bỏ qua” khác null như thế nào. Thêm test bắt đầu từ hàng có đủ dữ liệu, chỉ cập nhật một trường rồi đọc lại tất cả trường cần bảo toàn. Test chỉ kiểm tra display_name sẽ bỏ sót lỗi note trong ví dụ.

Đọc conflict target và các ràng buộc còn lại của bảng. UPSERT xử lý xung đột duy nhất được mô tả, không biến mọi lỗi NOT NULL, CHECK hoặc khóa ngoại thành UPDATE. Nếu schema có nhiều UNIQUE, thêm fixture cho từng đường xung đột thay vì mặc định ON CONFLICT(email) bao phủ tất cả.

Giới hạn cần xem riêng

Ví dụ chưa thêm trigger, bảng con hoặc kiểm tra hiệu ứng khóa ngoại của thao tác thay hàng. Đó là lý do cần rà schema thật trước khi dùng REPLACE cho dữ liệu liên kết. Bài không khẳng định kết quả số lượng hook hay trigger vì không thử chúng trong fixture.

Khi request chỉ muốn sửa một phần hàng, UPDATE hoặc một UPSERT có tập gán rõ ràng thường diễn đạt yêu cầu dễ kiểm tra hơn. Chọn câu lệnh theo dữ liệu phải giữ, rồi xác nhận cả nhánh tạo mới và nhánh cập nhật bằng test đọc lại.

Nguồn tham khảo

  • SQLite: thuật toán REPLACE trong ON CONFLICT
  • SQLite: UPSERT và excluded
  • Python: sqlite3 và binding

Tags: SQLSQLiteUPSERT
SummarizeShare234
Suong Nguyen

Suong Nguyen

Related Stories

Minh họa kiến trúc backend API gọn gàng cho sản phẩm nhỏ

SQL AND và OR: giữ điều kiện tenant bằng dấu ngoặc đúng chỗ

by Suong Nguyen
05/11/2026
0

Tái hiện lỗi lọc trạng thái làm lọt bản ghi tenant khác và kiểm tra nhóm điều kiện bằng dữ liệu đối chứng.

Minh họa kiến trúc backend API gọn gàng cho sản phẩm nhỏ

SQL LIKE: tìm dấu % và _ như ký tự thật bằng ESCAPE

by Suong Nguyen
04/11/2026
0

Kết hợp bind parameter và ESCAPE để ô tìm kiếm chứa % hoặc _ không vô tình trở thành một mẫu wildcard khác ý định người dùng.

Minh họa kiến trúc backend API gọn gàng cho sản phẩm nhỏ

SQL AVG gặp NULL: dữ liệu chưa có không tự trở thành số 0

by Suong Nguyen
03/11/2026
0

So sánh AVG(value), AVG(COALESCE(value, 0)) và COALESCE(AVG(value), 0) để thấy vị trí xử lý NULL có thể đổi ý nghĩa trung bình.

Minh họa kiến trúc backend API gọn gàng cho sản phẩm nhỏ

SQL EXISTS: lọc khách có đơn mà không nhân dòng khách hàng

by Suong Nguyen
02/11/2026
0

So sánh JOIN với EXISTS trên fixture có nhiều đơn cho một khách để giữ đúng đơn vị một dòng trong danh sách trả về.

0 0 votes
Article Rating
Subscribe
Login
Notify of
guest
guest
0 Comments
Oldest
Newest Most Voted
AE Dev

AE Dev mang đến các bài viết về lập trình, mobile app, backend, frontend, WordPress và công nghệ, phù hợp cho developer muốn học hỏi và cập nhật kiến thức thực tế.

Recent Posts

  • React Native ActivityIndicator: dừng spinner có cần bỏ cả vùng giữ chỗ?
  • Array.reduce thiếu initialValue: mảng rỗng lỗi, một phần tử không gọi callback
  • Android SharedPreferences.apply: đọc lại được chưa chứng minh đã ghi xong xuống đĩa

Categories

  • AI
  • Backend
  • Đánh giá
  • DevOps
  • Flutter
  • Hướng dẫn
  • Lập trình Mobile
  • Phỏng vấn
  • React Native
  • Uncategorized

Weekly Newsletter

  • Buy JNews
  • Support Forum
  • Pre-sale Question
  • Contact Us

© 2026 AE Dev - Blog công nghệ, lập trình và chia sẻ kiến thức IT.

Welcome Back!

Login to your account below

Forgotten Password?

Retrieve your password

Please enter your username or email address to reset your password.

Log In
No Result
View All Result
  • Trang chủ
  • AI
  • Mobile
  • React Native
  • Backend
  • DevOps
  • Hướng dẫn
  • Flutter

© 2026 AE Dev - Blog công nghệ, lập trình và chia sẻ kiến thức IT.

wpDiscuz
0
0
Would love your thoughts, please comment.x
()
x
| Reply