Overvej først og fremmest at gemme dataene på en normaliseret måde. Her er en god læsning:Er det virkelig så dårligt at gemme en afgrænset liste i en databasekolonne?
Nu - Forudsat følgende skema og data:
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');
Vi ønsker at finde produkter med variationer '12345'
og '34567'
. Det forventede resultat er 1. og 2. række.
Normaliseret skema - mange-til-mange-relation
I stedet for at gemme værdierne i en kommasepareret liste, skal du oprette en ny tabel, som kortlægger produkt-id'er med variationer:
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');
Valgforespørgslen ville være:
select distinct p.*
from products p
join products_upc_variations v on v.product_id = p.id
where v.upc_variation in ('12345', '34567');
Som du kan se - Med et normaliseret skema kan problemet løses med en ganske grundlæggende forespørgsel. Og vi kan effektivt bruge indekser.
"Udnyttelse" af et FULLTEXT INDEX
Med et FULLTEXT INDEX på (upc_variation)
du kan bruge:
select p.*
from products p
where match (upc_variation) against ('12345 34567');
Dette ser ret "pænt" ud og er sandsynligvis effektivt. Men selvom det virker for dette eksempel, ville jeg ikke føle mig tryg ved denne løsning, fordi jeg ikke kan sige præcist, hvornår den ikke virker.
Brug af JSON_OVERLAPS()
Siden MySQL 8.0.17 kan du bruge JSON_OVERLAPS() . Du bør enten gemme værdierne som et JSON-array eller konvertere listen til JSON "on the fly":
select p.*
from products p
where json_overlaps(
'["12345","34567"]',
concat('["', replace(upc_variation, ',', '","'), '"]')
);
Intet indeks kan bruges til dette. Men det kan heller ikke for FIND_IN_SET()
.
Brug af JSON_TABLE()
Siden MySQL 8.0.4 kan du bruge JSON_TABLE() at generere en normaliseret repræsentation af dataene "on the fly". Her igen vil du enten gemme dataene i et JSON-array eller konvertere listen til JSON i forespørgslen:
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');
Intet indeks kan bruges her. Og dette er nok den langsomste løsning af alle præsenteret i dette svar.
RLIKE / REGEXP
Du kan også bruge et regulært udtryk :
select p.*
from products p
where p.upc_variation rlike '(^|,)(12345|34567)(,|$)'
Se demo af alle forespørgsler på dbfiddle.uk