Pertama-tama pertimbangkan untuk menyimpan data dengan cara yang dinormalisasi. Ini bacaan yang bagus:Apakah menyimpan daftar yang dibatasi dalam kolom database benar-benar buruk?
Sekarang - Dengan asumsi skema dan data berikut:
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');
Kami ingin mencari produk dengan variasi '12345'
dan '34567'
. Hasil yang diharapkan adalah baris ke-1 dan ke-2.
Skema yang dinormalisasi - relasi banyak-ke-banyak
Daripada menyimpan nilai dalam daftar yang dipisahkan koma, buat tabel baru, yang memetakan ID produk dengan variasi:
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');
Kueri pemilihannya adalah:
select distinct p.*
from products p
join products_upc_variations v on v.product_id = p.id
where v.upc_variation in ('12345', '34567');
Seperti yang Anda lihat - Dengan skema yang dinormalisasi, masalahnya dapat diselesaikan dengan kueri yang cukup mendasar. Dan kita dapat menggunakan indeks secara efektif.
"Mengeksploitasi" INDEKS TEKS LENGKAP
Dengan INDEKS FULLTEXT pada (upc_variation)
Anda dapat menggunakan:
select p.*
from products p
where match (upc_variation) against ('12345 34567');
Ini terlihat cukup "cantik" dan mungkin efisien. Tetapi meskipun berhasil untuk contoh ini, saya tidak akan merasa nyaman dengan solusi ini, karena saya tidak bisa mengatakan dengan tepat, kapan itu tidak berhasil.
Menggunakan JSON_OVERLAPS()
Sejak MySQL 8.0.17 Anda dapat menggunakan JSON_OVERLAPS() . Anda harus menyimpan nilai sebagai larik JSON, atau mengonversi daftar menjadi JSON "on the fly":
select p.*
from products p
where json_overlaps(
'["12345","34567"]',
concat('["', replace(upc_variation, ',', '","'), '"]')
);
Tidak ada indeks yang dapat digunakan untuk ini. Tapi juga tidak bisa untuk FIND_IN_SET()
.
Menggunakan JSON_TABLE()
Sejak MySQL 8.0.4 Anda dapat menggunakan JSON_TABLE() untuk menghasilkan representasi data yang dinormalisasi "on the fly". Di sini sekali lagi Anda akan menyimpan data dalam larik JSON, atau mengonversi daftar ke JSON dalam kueri:
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');
Tidak ada indeks yang dapat digunakan di sini. Dan ini mungkin solusi paling lambat dari semua yang disajikan dalam jawaban ini.
RLIKE / REGEXP
Anda juga dapat menggunakan ekspresi reguler :
select p.*
from products p
where p.upc_variation rlike '(^|,)(12345|34567)(,|$)'
Lihat demo semua kueri di dbfiddle.uk