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ỏ

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

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

Người dùng nhập tên rate_100%, nhưng kết quả còn có rateX100Y. Query đã dùng parameter binding, vậy vì sao vẫn khớp nhiều hơn mong muốn? Dữ liệu đang được nhận như pattern của LIKE, trong đó phần trăm và gạch dưới có ý nghĩa riêng.

Bài đối chiếu PostgreSQL 17, SQLite và Python sqlite3 ngày 04/11/2026. Fixture chạy bằng Python 3.9.6 với SQLite 3.54.0 trong database bộ nhớ. Chưa chạy PostgreSQL server, chưa kiểm tra collation tiếng Việt hoặc hiệu năng tìm kiếm.

Phân biệt cú pháp query và cú pháp pattern

Trong LIKE, % khớp một chuỗi bất kỳ, còn _ khớp một ký tự. Parameter binding đưa giá trị vào query theo hợp đồng của driver; nó không tự biến các wildcard bên trong giá trị thành ký tự literal.

Ví dụ này chọn dấu ! làm escape character và ghi rõ ESCAPE '!' trong query. Khi người dùng nhập chính dấu chấm than, cần escape nó trước, rồi mới escape % và _. Nếu làm thứ tự ngược, các dấu escape vừa thêm có thể bị xử lý lần nữa.

Một fixture có hai tên dễ khớp nhầm

Lưu thành like_demo.py, chạy python3 like_demo.py:

import sqlite3

def contains_pattern(text):
    if not text:
        raise ValueError("Can tu khoa khong rong")
    literal = text.replace("!", "!!").replace("%", "!%").replace("_", "!_")
    return "%" + literal + "%"

db = sqlite3.connect(":memory:")
db.execute("CREATE TABLE demo_names (id INTEGER PRIMARY KEY, name TEXT)")
db.executemany("INSERT INTO demo_names VALUES (?, ?)", [
    (1, "rate_100%"),
    (2, "rateX100Y"),
    (3, "wow!x"),
    (4, "wowx"),
])
term = "rate_100%"
naive = db.execute(
    "SELECT id FROM demo_names WHERE name LIKE ? ORDER BY id",
    ("%" + term + "%",),
).fetchall()
literal = db.execute(
    "SELECT id FROM demo_names WHERE name LIKE ? ESCAPE '!' ORDER BY id",
    (contains_pattern(term),),
).fetchall()
assert naive == [(1,), (2,)]
assert literal == [(1,)]
print(naive, literal)

Hai kết quả đã được kiểm tra. Query đầu không vi phạm cách bind của sqlite3, nhưng diễn đạt tìm theo wildcard. Query sau diễn đạt tìm chuỗi chứa đúng các ký tự mà term cung cấp, trong phạm vi semantics LIKE của database đang dùng.

Hai dấu % bọc ngoài do ứng dụng chủ ý thêm để tìm chuỗi con. Phần nội dung người dùng nhập bên trong đã được escape. Không ghép term trực tiếp thành một đoạn SQL; placeholder vẫn giữ vai trò riêng của nó.

Kiểm tra cả ký tự escape

Thêm vào cùng file trước khi đóng connection:

found = db.execute(
    "SELECT id FROM demo_names WHERE name LIKE ? ESCAPE '!' ORDER BY id",
    (contains_pattern("wow!x"),),
).fetchall()
assert found == [(3,)]
assert contains_pattern("!%_") == "%!!!%!_%"
db.close()

Các assertion đã chạy qua. Một hàm chỉ thay % và _ mà bỏ quên chính escape character sẽ không bảo toàn ý nghĩa mọi đầu vào. Giữ case này trong kiểm tra của helper để tránh sửa một ký tự rồi làm hỏng ký tự khác.

Demo từ chối chuỗi rỗng để tránh vô tình biến thao tác ấy thành tìm tất cả. Đây là chính sách của ví dụ; sản phẩm có thể chọn cách khác, nhưng nên quyết định rõ ở lớp nhận request.

Giới hạn khi chuyển sang database thật

Placeholder ? thuộc cách gọi sqlite3 ở đây. Driver PostgreSQL có cú pháp binding riêng, nên chuyển cả query lẫn cách truyền arguments theo driver. Cơ chế phân biệt hoa thường, dấu và locale cũng cần kiểm tra trên database thật; fixture ASCII không chứng minh một bộ tìm kiếm tiếng Việt đã hoàn chỉnh.

Pattern bắt đầu bằng wildcard có thể không phù hợp một chiến lược index đang dùng. Bài không đo execution plan và không kết luận LIKE là lựa chọn tốt nhất cho mọi ô tìm kiếm. Nếu cần tìm từ, xếp hạng hoặc truy vấn lớn, hãy đánh giá công cụ tìm kiếm theo yêu cầu đó.

Chốt trước người dùng đang nhập một pattern hay một chuỗi cần tìm nguyên nghĩa. Sau đó giữ parameter binding và escape pattern như hai bước có mục đích rõ ràng.

Nguồn tham khảo

  • PostgreSQL 17: LIKE và ESCAPE
  • SQLite: LIKE và ký tự escape
  • Python: sqlite3 và placeholders

Tags: PythonSQLTìm kiếm
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 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ề.

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

Lọc thời gian trong SQL: dùng khoảng nửa mở để không trùng biên

by Suong Nguyen
01/11/2026
0

So sánh BETWEEN với điều kiện >= đầu kỳ và < đầu kỳ kế tiếp bằng fixture ở đúng ranh giới tháng.

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

SQLite: có FOREIGN KEY nhưng vẫn lọt dữ liệu mồ côi?

by Suong Nguyen
31/10/2026
0

Bật và đọc lại PRAGMA foreign_keys trên từng connection SQLite, rồi thử cả insert sai lẫn kiểm tra dữ liệu đã tồn tại.

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

Array.slice và splice: end index khác deleteCount thế nào?

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 ScrollView: tạo nút về đầu bằng ref và scrollTo
  • Array.isArray và instanceof: mảng từ realm khác được kiểm tra thế nào?
  • Android LayoutParams nhận pixel: đổi dp đúng một lần

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