Trước hết hãy xem xét để lưu trữ dữ liệu theo cách chuẩn hóa. Đây là một bài đọc hay: Việc lưu trữ danh sách được phân tách trong cột cơ sở dữ liệu có thực sự tồi tệ không?
Bây giờ - Giả sử lược đồ và dữ liệu sau:
create table products (
id int auto_increment,
upc varchar(50),
upc_variation text,
primary key (id),
index (upc)
);
insert into products (upc, upc_variation) values
('01234', '01234,12345,23456'),
('56789', '45678,34567'),
('056789', '045678,034567');
Chúng tôi muốn tìm các sản phẩm có các biến thể '12345'
và '34567'
. Kết quả mong đợi là hàng thứ nhất và thứ hai.
Lược đồ chuẩn hóa - quan hệ nhiều-nhiều
Thay vì lưu trữ các giá trị trong danh sách được phân tách bằng dấu phẩy, hãy tạo một bảng mới, bảng này ánh xạ ID sản phẩm với các biến thể:
create table products_upc_variations (
product_id int,
upc_variation varchar(50),
primary key (product_id, upc_variation),
index (upc_variation, product_id)
);
insert into products_upc_variations (product_id, upc_variation) values
(1, '01234'),
(1, '12345'),
(1, '23456'),
(2, '45678'),
(2, '34567'),
(3, '045678'),
(3, '034567');
Truy vấn được chọn sẽ là:
select distinct p.*
from products p
join products_upc_variations v on v.product_id = p.id
where v.upc_variation in ('12345', '34567');
Như bạn thấy - Với một lược đồ chuẩn hóa, vấn đề có thể được giải quyết bằng một truy vấn khá cơ bản. Và chúng ta có thể sử dụng các chỉ số một cách hiệu quả.
"Khai thác" CHỈ SỐ FULLTEXT
Với FULLTEXT INDEX trên (upc_variation)
bạn có thể sử dụng:
select p.*
from products p
where match (upc_variation) against ('12345 34567');
Điều này trông khá "đẹp" và có lẽ là hiệu quả. Nhưng mặc dù nó hoạt động cho ví dụ này, tôi sẽ không cảm thấy thoải mái với giải pháp này, vì tôi không thể nói chính xác, khi nào nó không hoạt động.
Sử dụng JSON_OVERLAPS ()
Kể từ MySQL 8.0.17, bạn có thể sử dụng JSON_OVERLAPS () . Bạn nên lưu trữ các giá trị dưới dạng một mảng JSON hoặc chuyển đổi danh sách thành JSON "nhanh chóng":
select p.*
from products p
where json_overlaps(
'["12345","34567"]',
concat('["', replace(upc_variation, ',', '","'), '"]')
);
Không có chỉ mục nào có thể được sử dụng cho việc này. Nhưng không thể đối với FIND_IN_SET()
.
Sử dụng JSON_TABLE ()
Kể từ MySQL 8.0.4, bạn có thể sử dụng JSON_TABLE () để tạo ra một biểu diễn dữ liệu được chuẩn hóa một cách "nhanh chóng". Ở đây một lần nữa, bạn sẽ lưu trữ dữ liệu trong một mảng JSON hoặc chuyển đổi danh sách thành JSON trong truy vấn:
select distinct p.*
from products p
join json_table(
concat('["', replace(p.upc_variation, ',', '","'), '"]'),
'$[*]' columns (upcv text path '$')
) v
where v.upcv in ('12345', '34567');
Không có chỉ mục nào có thể được sử dụng ở đây. Và đây có lẽ là giải pháp chậm nhất trong số tất cả các giải pháp được trình bày trong câu trả lời này.
RLIKE / REGEXP
Bạn cũng có thể sử dụng biểu thức chính quy :
select p.*
from products p
where p.upc_variation rlike '(^|,)(12345|34567)(,|$)'
Xem bản trình diễn tất cả các truy vấn trên dbfiddle.uk