На първо място помислете за съхраняване на данните по нормализиран начин. Ето едно добро четиво:Толкова ли лошо ли е съхраняването на разделен списък в колона на база данни?
Сега - Приемаме следната схема и данни:
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');
Искаме да намерим продукти с вариации '12345'
и '34567'
. Очакваният резултат е 1-ви и 2-ри ред.
Нормализирана схема – релация много към много
Вместо да съхранявате стойностите в списък, разделен със запетая, създайте нова таблица, която съпоставя идентификаторите на продукти с вариации:
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');
Заявката за избор ще бъде:
select distinct p.*
from products p
join products_upc_variations v on v.product_id = p.id
where v.upc_variation in ('12345', '34567');
Както виждате - С нормализирана схема проблемът може да бъде решен с доста елементарна заявка. И можем ефективно да използваме индекси.
„Експлоатация“ на ПЪЛТЕКСТОВ ИНДЕКС
С ПЪЛЕН ТЕКСТОВ ИНДЕКС на (upc_variation)
можете да използвате:
select p.*
from products p
where match (upc_variation) against ('12345 34567');
Това изглежда доста "красиво" и вероятно е ефективно. Но въпреки че работи за този пример, не бих се чувствал комфортно с това решение, защото не мога да кажа точно кога не работи.
Използване на JSON_OVERLAPS()
От MySQL 8.0.17 можете да използвате JSON_OVERLAPS() . Трябва или да съхранявате стойностите като JSON масив, или да конвертирате списъка в JSON "в движение":
select p.*
from products p
where json_overlaps(
'["12345","34567"]',
concat('["', replace(upc_variation, ',', '","'), '"]')
);
За това не може да се използва индекс. Но нито за FIND_IN_SET()
.
Използване на JSON_TABLE()
От MySQL 8.0.4 можете да използвате JSON_TABLE() за генериране на нормализирано представяне на данните "в движение". Тук отново бихте съхранили данните в JSON масив, или ще преобразувате списъка в JSON в заявката:
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');
Тук не може да се използва индекс. И това е може би най-бавното решение от всички представени в този отговор.
RLIKE / REGEXP
Можете също да използвате регулярен израз :
select p.*
from products p
where p.upc_variation rlike '(^|,)(12345|34567)(,|$)'
Вижте демо на всички заявки в dbfiddle.uk